Bagisto handles "multiple stores" differently from platforms that spin up entirely separate installations per site. Its Laravel-based architecture includes a built-in multi-channel system where a single codebase and single database serve several storefronts, each with its own theme, currencies, locales, root category, and hostname. Before you clone directories or create extra databases, it is worth understanding exactly what the native model covers, because most requests for a "second store" on Bagisto are satisfied without a second deployment at all.
What Bagisto supports natively: channels, not clones
A Bagisto channel represents a distinct storefront that shares the same admin backend, product catalog pool, customers table, and order pipeline. From the admin panel at /admin/settings/channels, you create a channel and assign it a hostname, a default theme, a root category, allowed currencies, and allowed locales. Each channel can point to a different subdomain or domain, so shop.example.com and store.example.com can render entirely different catalogs and branding while running from one installation under /home/username/public_html.
This design means the following scenarios are handled without any extra instance:
- Multiple brands or regional storefronts sharing inventory and a single admin login.
- Multi-language and multi-currency sites using locales configured under
/admin/settings/localesand currencies under/admin/settings/currencies. - A wholesale vs. retail split, where each channel has its own theme and root category to expose different product trees.
When you assign a hostname to a channel, Bagisto reads the incoming HTTP_HOST and resolves the active channel. That resolution only works if the domain or subdomain actually reaches the same document root. On shared hosting you arrange that in cPanel under Domains → Create A New Domain (or Subdomains on older layouts), or in DirectAdmin under Account Manager → Domain Setup and Subdomain Management. The key step most people miss: point the new subdomain's document root at the existing Bagisto public directory rather than letting the panel create a fresh empty folder.
For example, if Bagisto lives at /home/username/bagisto with its web root at /home/username/bagisto/public, create the subdomain and then set its Document Root to /home/username/bagisto/public. In cPanel's Create A New Domain dialog you can uncheck "Share document root" and type the path manually; in DirectAdmin you adjust the document root after creation from Domain Setup. Once DNS resolves and the subdomain serves the same public/index.php, add that exact hostname to the channel in the admin panel and clear the cache.
Wiring subdomains, APP_URL, and cache to the channel model">Wiring subdomains, APP_URL, and cache
Because every channel shares one Laravel application, the .env file at /home/username/bagisto/.env holds a single APP_URL. Set it to your primary storefront, for instance APP_URL=https://shop.example.com. Channel-specific hostnames do not go in .env; they belong on the channel records inside the database via the admin UI. Bagisto builds absolute URLs per request based on the resolved channel, so a mismatched APP_URL mostly affects queued jobs, emails, and CLI-generated links rather than live page rendering.
After adding or changing a channel hostname you must clear compiled configuration and route caches, otherwise the old channel mapping persists. Without SSH you can trigger this two ways on shared hosting. If your plan exposes a terminal in cPanel, the Artisan commands are cleanest. If not, delete the stale cache files directly through File Manager:
bootstrap/cache/config.php
bootstrap/cache/routes-v7.php
bootstrap/cache/services.php
storage/framework/cache/data/ (contents)
storage/framework/views/ (compiled blade, safe to empty)
Removing bootstrap/cache/config.php forces Laravel to re-read .env and the database channel table on the next request. Never delete the storage/framework subdirectories themselves, only their cached contents, and confirm the storage tree remains writable (permission 755 on directories, 644 on files is typical for CloudLinux).
SSL matters for each hostname. Every channel domain and subdomain needs its own certificate. In cPanel use Security → SSL/TLS Status and run AutoSSL for each new subdomain; in DirectAdmin use Account Manager → SSL Certificates and issue a free Let's Encrypt certificate covering the subdomain. Force HTTPS at the edge with a rule in the public/.htaccess file that LiteSpeed honors:
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Using %{HTTP_HOST} instead of a hardcoded domain keeps the redirect correct across all channel hostnames sharing that document root. PHP limits for image uploads and catalog imports are set per account through the MultiPHP INI Editor in cPanel or Select PHP Version → Options in DirectAdmin, or by placing a .user.ini beside public/index.php with values such as memory_limit = 512M and max_execution_time = 300.
When you genuinely need a separate Bagisto instance
Channels break down when two stores must be truly isolated: different Bagisto versions, different extension sets, separate customer databases for legal or client-ownership reasons, or independent staging. In those cases you deploy a second, fully independent Bagisto with its own directory, database, and .env. This is a genuine second instance, not a channel.
Create a separate database and user in cPanel under MySQL Databases or DirectAdmin under Account Manager → MySQL Management, giving the new user ALL PRIVILEGES on the new schema only. Extract a fresh Bagisto release into a distinct path such as /home/username/store2, then point a domain or subdomain (for example store2.example.com) at /home/username/store2/public. Each instance carries its own .env:
APP_URL=https://store2.example.com
DB_DATABASE=username_store2
DB_USERNAME=username_store2usr
DB_PASSWORD=your-strong-password
APP_KEY= (generated during install)
Confirm both instances run the PHP version each release requires. The PHP Selector (CloudLinux) or MultiPHP Manager lets you assign a PHP version per domain, so an older store on PHP 8.1 and a newer one on PHP 8.2 can coexist. Enable the extensions Bagisto needs for each version: pdo_mysql, gd or imagick, intl, bcmath, fileinfo, curl, and zip. Missing extensions surface as blank pages or fatal errors; check the local storage/logs/laravel.log and the account error_log in the document root when a fresh instance fails to load.
Isolated instances also mean isolated scheduled work. Bagisto uses Laravel's scheduler for tasks like abandoned-cart and index refresh. Add a cron entry per instance under cPanel Cron Jobs or DirectAdmin Create Cron Job, pointing at each instance's own artisan:
/usr/local/bin/php /home/username/store2/artisan schedule:run >/dev/null 2>&1
Set that to run every minute. Repeat with the primary instance's path for its own schedule. Keeping the cron paths, databases, and directories strictly separate is what preserves the isolation that made a second instance necessary in the first place.
Choose the model deliberately: reach for native channels whenever stores share catalog, customers, and admin, and reserve full separate instances for version, ownership, or true data-isolation requirements. Mixing the two on one account is fine, provided each instance and each channel hostname resolves to the correct document root and carries a valid certificate.