[Blog](https://deploywithox.com/blog) [Guides](https://deploywithox.com/blog#c-guides) # How to self-host Bagisto without Docker on your own VPS Run Bagisto v2.5.0 on your own server with ox: Laravel, MariaDB, a queue worker, the scheduler and a first admin made on deploy, from the config that ran. Saurav Sharma Oct 17, 2026 11 min read On this page - [What you need](https://deploywithox.com/blog/self-host-bagisto#what-you-need) - [The steps](https://deploywithox.com/blog/self-host-bagisto#steps) - [The ox.toml, line by line](https://deploywithox.com/blog/self-host-bagisto#config) - [The database, the queue and the scheduler](https://deploywithox.com/blog/self-host-bagisto#services) - [Updates and backups](https://deploywithox.com/blog/self-host-bagisto#updates) - [Frequently asked questions](https://deploywithox.com/blog/self-host-bagisto#faq) To self-host Bagisto you need an Ubuntu server, your own fork of the Bagisto repo with three files added, and 16 variables, six of them for the database ox sets up. In our run the first deploy finished in about 2 minutes, and ox set up MariaDB, a queue worker and a timer for Laravel's scheduler. **How we tested** On Oct 17, 2026, ox deployed Bagisto v2.5.0 from commit [`4e7f0862`](https://github.com/bagisto/bagisto/commit/4e7f08621904a665b378112e1a39b837c27a4c3f) on a fresh DigitalOcean server with 8 vCPUs and 16 GB of memory. The run then redeployed it, backed it up and rebooted the server. Then a separate test agent, one that did not write the config, signed in, added a product in the admin and saw it on the storefront with its price and an Add To Cart button, and checked the app's services and its backup. [Bagisto](https://github.com/bagisto/bagisto) is an open-source online store built on Laravel and Vue.js by Webkul, under the MIT license. Shoppers use the storefront at the site's address. The shop owner uses the admin panel at `/admin`. This guide covers the single store with the default theme. ox deployed Bagisto v2.5.0 (commit 4e7f0862) on Oct 17, 2026 on a fresh DigitalOcean server with 8 vCPUs and 16 GB of memory; the first deploy took 2 minutes and a redeploy took 38 seconds. - **First deploy:** 2 minutes, from adding the project until the app passed the run's first checks. - **Redeploy of the same commit:** 38 seconds, until the app passed the checks again. - **Server:** DigitalOcean, 8 vCPUs and 16 GB of memory (`s-8vcpu-16gb`), with this app alone on it. Every one of the 85 apps we deployed ran on this one size, so it was not picked for this app. - **Memory in use after the reboot:** 898 MB of 15986 MB, for the whole server: Ubuntu, the app, its services, Caddy and the ox agent. On a server with no apps, ox's tests on real servers hold the agent to 40 MB or less and Caddy to 50 MB or less. - **Services ox runs for it:** `mysql`. - **Background processes:** `queue`, each its own systemd unit beside the web process. - **Scheduled jobs:** `schedule`. - **Source:** [github.com/bagisto/bagisto](https://github.com/bagisto/bagisto) at v2.5.0, commit [`4e7f0862`](https://github.com/bagisto/bagisto/commit/4e7f08621904a665b378112e1a39b837c27a4c3f). The Bagisto storefront home page with its default Velocity theme The Bagisto admin dashboard after signing in The storefront page of the new product Ox Verify Mug at $19.99 with an Add To Cart button ## What you need - An Ubuntu LTS server. The run used the size in the facts above and measured the memory there. Bagisto 2.5 asks for PHP 8.4 or newer, and ox serves PHP apps on PHP 8.5. - A GitHub account to fork `bagisto/bagisto`, and an ox account, which you open by signing in with GitHub. - An email address such as `you@example.com` and a password for the first admin. Bagisto sends order and welcome mail through SMTP, and the run pointed it at a test mail catcher, so real mail was not tested. Checkout, orders, online payments and product image uploads were not tested either. ## The steps Open the ox dashboard, press Add a server, and run the command it prints in a root shell on the new server. It sets up the ox agent and Caddy. Fork `bagisto/bagisto` and check out the tag `v2.5.0`, commit `4e7f0862`, the release the run deployed. Add `ox.toml`, `ox-install.php` and `php.ini` at the root of the fork, as shown below, then commit and push. Write `bagisto.env` on your laptop with one `KEY=value` line for each key in the variables section. Laravel's `APP_KEY` is `base64:` and 32 random bytes in base64. Set `APP_URL` to `${PUBLIC_URL}`. The example file also lists keys for features this setup does not use, such as AWS and R2 storage, Reverb and Pusher. ox asks for each one, so the run marked them Not needed with `unset KEY` lines, and `ox review` stops and names any you leave out. With the [ox CLI](https://deploywithox.com/docs/cli) signed in, run: Shell ```bash ox new /bagisto ox review bagisto --from-file bagisto.env --wait ``` The review saves your values and starts the first deploy, and `--wait` prints the log until it ends. The [quickstart](https://deploywithox.com/docs/quickstart) shows the same two steps in the browser. When the deploy is green, open the project's address plus `/admin/login` and sign in with `ADMIN_EMAIL` and `ADMIN_PASSWORD`. The store starts with no products. Open Catalog, Products, Create Product, and pick the Simple type, the Default attribute family and a SKU. Bagisto requires a name, URL key, short description, description, price and weight. Switch on Status and Visible Individually and save. The test agent then loaded `/` on the storefront and saw the product with its price and an Add To Cart button. Prices come through the queue, so a new product can take a few seconds to appear. The run served Bagisto on its preview address only. A domain of your own was not part of it, and the [domains page](https://deploywithox.com/docs/operate/domains) covers how ox adds one. ## The ox.toml, line by line TOML ```toml # Bagisto 2.5 (Laravel 13, MySQL) on ox: FrankenPHP serves public/ (detected), # MariaDB, the queue worker and the Laravel scheduler. required_variables = ["ADMIN_EMAIL", "ADMIN_PASSWORD"] [build] # The release's own production image installs Composer's packages only: the # storefront, admin and installer builds are committed under public/, and # the root package.json has no lockfile. --ignore-platform-req=php: ox # serves PHP 8.5, Bagisto requires ^8.4, and the locked # phpoffice/phpspreadsheet caps PHP below 8.5. install = "composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-req=php" commands = [ # What artisan storage:link makes: product and theme images under # storage/app/public served at /storage. "ln -sfn ../storage/app/public public/storage", # Bagisto counts itself installed only when a .env file exists (its # settings come from ox's environment, so the file stays empty); without # it a lost storage/installed marker would reopen the web installer. "touch .env", ] # bagisto:install wipes the database every time it runs, so ox-install.php # does its first-run steps once instead: see that file. migrate = "php artisan migrate --force && php ox-install.php" [workers] # Product price and stock indexes, catalog rules, imports and mail go # through Laravel's queue, the database one by default (config/queue.php). queue = { run = "exec php artisan queue:work --sleep=3 --tries=3 --max-time=3600", stop_timeout = "5m" } [cron] # Laravel's scheduler: the daily price and catalog-rule indexes, invoice # reminders, campaigns and exchange rates. schedule = { schedule = "* * * * *", run = "php artisan schedule:run" } [services] mysql = {} [storage] # Laravel writes storage/: uploads, the install marker, logs, compiled views. keep = ["storage"] ``` There is no `[app]` section: ox sees `composer.json` and `artisan`, starts FrankenPHP on `public`, and takes the health page `/up` from `bootstrap/app.php`. - `required_variables` names the admin's email and password. Bagisto never reads them, `ox-install.php` does, and the review asks for them first. - `install` runs Composer without dev packages and runs no npm, because the storefront and admin builds are committed under `public`. `--ignore-platform-req=php` is there because the locked `phpoffice/phpspreadsheet` 1.30.6 accepts PHP below 8.5 and ox serves 8.5, so Composer stops without it. The run's checks passed with it, and ox's docs say the app may break on a PHP it does not list. - The first build command links `public/storage` to `storage/app/public`, as `artisan storage:link` does, so images are served at `/storage`. `touch .env` makes an empty file, because Bagisto's install check needs one to exist while its settings come from ox's environment. Without it, a lost `storage/installed` marker would send visitors back to the web installer. - `migrate` runs Laravel's migrations, then `ox-install.php`. It leaves out `php artisan bagisto:install`, which Bagisto's own production image runs, because that command wipes the database first. - The `queue` worker runs `queue:work`. Bagisto queues price and stock index updates, catalog rules, imports and mail, and `config/queue.php` keeps that queue in the database. `--max-time=3600` ends the worker after an hour, and ox's service starts it again. `stop_timeout` gives a running job five minutes to finish when the worker stops. - The `schedule` job runs `php artisan schedule:run` every minute, and Laravel decides what is due. Bagisto schedules daily price and catalog rule indexes, invoice overdue reminders, marketing campaigns, exchange rates and price snapshots every 15 minutes. - `mysql = {}` asks for a MariaDB that belongs to this project alone. - `keep = ["storage"]` names the folder Laravel writes while it runs: uploads, logs, compiled views and the install marker. A PHP release is read-only, so ox links `storage` to the project's data folder, which stays writable across deploys. `ox-install.php` Code ``` make(Kernel::class)->bootstrap(); if (DB::table('admins')->exists()) { echo "Bagisto is already installed\n"; } else { $email = (string) getenv('ADMIN_EMAIL'); $password = (string) getenv('ADMIN_PASSWORD'); if ($email === '' || $password === '') { fwrite(STDERR, "ADMIN_EMAIL and ADMIN_PASSWORD must be set for the first deploy\n"); exit(1); } // One transaction: a seed that stops part way leaves nothing behind, and // the next deploy starts the first run again. DB::transaction(function () use ($email, $password) { // The installer's own seed configuration (Installer::getSeederConfiguration). $locale = config('app.locale'); $currency = config('app.currency'); app(DatabaseSeeder::class)->run([ 'default_locale' => $locale, 'allowed_locales' => [$locale], 'default_currency' => $currency, 'allowed_currencies' => [$currency], 'skip_admin_creation' => true, ]); // createAdminUser reports its error to the log and returns false. if (! app(DatabaseManager::class)->createAdminUser([ 'name' => 'Admin', 'email' => $email, 'password' => $password, ])) { throw new RuntimeException('the first admin was not created: see the error above'); } }); echo "Bagisto installed: base data seeded, admin {$email} created\n"; } // The marker Bagisto's installer writes last: with it, the installer's // middleware takes the store as installed without asking the database. touch(storage_path('installed')) || exit(1); ``` `php.ini` Code ``` ; PHP reads php.ini from the folder it starts in, the release. ox sets the ; upload limits ([app] max_upload); these two are what Bagisto's own ; production image (docker/production/shared/php.ini) sets for the web: a ; product photo resized with GD and a large import or invoice PDF outgrow ; PHP's 128M and 30 seconds. memory_limit = 512M max_execution_time = 300 ``` `ox-install.php` does once what Bagisto's installer does after its migrations. When the `admins` table already has a row, it prints that Bagisto is installed and skips the seeding. Otherwise it seeds the base data in one transaction, with the app's own locale and currency, and creates the admin from `ADMIN_EMAIL` and `ADMIN_PASSWORD`. A seed that stops part way leaves nothing behind, and the next deploy starts again. Last, it writes `storage/installed`, the marker Bagisto's installer writes. It runs only from the command line, and the web root is `public`, so nobody can fetch it. `php.ini` sets `memory_limit` to 512M and `max_execution_time` to 300 seconds, the numbers in Bagisto's own production `php.ini`, since resizing a photo or reading a large import can pass PHP's defaults. ox sets the upload limits itself. Shell ```bash ADMIN_EMAIL ADMIN_PASSWORD APP_KEY APP_URL DB_CONNECTION DB_HOST DB_PORT DB_DATABASE DB_USERNAME DB_PASSWORD LOG_CHANNEL MAIL_HOST MAIL_PORT MAIL_FROM_ADDRESS ADMIN_MAIL_ADDRESS CONTACT_MAIL_ADDRESS ``` `ADMIN_EMAIL` and `ADMIN_PASSWORD` are the first admin. `APP_KEY` is Laravel's encryption key. `APP_URL` is the address the app builds links from, and ox's `${PUBLIC_URL}` gives it ([variables](https://deploywithox.com/docs/operate/variables) explains references). `LOG_CHANNEL` set to `stderr` puts Laravel's log in the project's logs, which `ox logs bagisto` reads. `MAIL_HOST` and `MAIL_PORT` name the SMTP server, a test mail catcher in the run. `MAIL_FROM_ADDRESS` sends every mail, `ADMIN_MAIL_ADDRESS` gets order notifications, and `CONTACT_MAIL_ADDRESS` shows in mail footers. `DB_CONNECTION` is `mysql`, and the other five `DB_` keys are references to the project's database, such as `${mysql.host}` ([configuration page](https://deploywithox.com/docs/config)). ## The database, the queue and the scheduler The database is MariaDB 11.8, which ox calls `mysql`. It runs for this project alone. After the test agent saved its product, the queue's log showed the price index job finish, and the scheduler's log showed a price snapshot job finish. ## Updates and backups To move to a new release, merge its tag into your fork and push, or run `ox deploy bagisto --wait`. ox builds the new release in its own folder, runs `migrate`, and switches only when the new one answers. Then `ox-install.php` finds the admin and changes nothing. Bagisto's `UPGRADE.md` lists the high impact changes of an upgrade, so read it first. The [rollback page](https://deploywithox.com/docs/operate/rollback) covers a release that goes wrong. Backups need no setup. The run's backup, made with Back up now, held a MariaDB archive and an archive of the kept `storage` folder. Run `ox backups bagisto` to list them, or `ox backups bagisto now mysql` to make one. A restore was not tested. The [backups page](https://deploywithox.com/docs/operate/backups) covers copies in an S3 bucket. ## Frequently asked questions ### Do I need Docker to self-host Bagisto? No. Bagisto publishes Docker images and a compose file, but the store is a Laravel app that runs without them. ox installs its packages with Composer on your server, serves it with FrankenPHP and adds a MariaDB service beside it. To update, push to your fork or run one deploy command. ### How do I create the first Bagisto admin account? Put ADMIN_EMAIL and ADMIN_PASSWORD in your variables before the first deploy. The migrate line runs ox-install.php, which seeds the store and creates that admin, and you sign in at /admin/login. Bagisto's installer defaults to admin@example.com with the password admin123, which this setup never creates. ### Which database does Bagisto use? Bagisto's Docker images pair it with MySQL 8.0, MariaDB 10.11 or PostgreSQL 16. The run used the mysql service ox adds to the project, which is MariaDB 11.8, and ox fills the DB_ keys from it. PostgreSQL was not tested here. ### How much memory does self-hosted Bagisto use? After the reboot, the whole server in the run used the memory the facts show. That counts MariaDB, the queue worker, Caddy, the ox agent and the run's test mail catcher as well as Bagisto. ox works on any server from 1 GB up and names memory when memory is why an app stopped. New to running apps on your own server? [How ox self-hosts your apps](https://deploywithox.com/self-hosting) shows what ox sets up on your server. On this page - [What you need](https://deploywithox.com/blog/self-host-bagisto#what-you-need) - [The steps](https://deploywithox.com/blog/self-host-bagisto#steps) - [The ox.toml, line by line](https://deploywithox.com/blog/self-host-bagisto#config) - [The database, the queue and the scheduler](https://deploywithox.com/blog/self-host-bagisto#services) - [Updates and backups](https://deploywithox.com/blog/self-host-bagisto#updates) - [Frequently asked questions](https://deploywithox.com/blog/self-host-bagisto#faq) - [Do I need Docker to self-host Bagisto?](https://deploywithox.com/blog/self-host-bagisto#faq-docker) - [How do I create the first Bagisto admin account?](https://deploywithox.com/blog/self-host-bagisto#faq-admin) - [Which database does Bagisto use?](https://deploywithox.com/blog/self-host-bagisto#faq-database) - [How much memory does self-hosted Bagisto use?](https://deploywithox.com/blog/self-host-bagisto#faq-memory) Written by Saurav Sharma Builds ox. Writes about running apps on servers you own, without Docker and without a platform bill. [Older post How to self-host Campfire without Docker on your own VPS](https://deploywithox.com/blog/self-host-campfire) [Newer post How to self-host authentik without Docker on your own VPS](https://deploywithox.com/blog/self-host-authentik) ## Keep reading - Guides [Self-Hosted Open-Source Apps Deployed with ox: All 85](https://deploywithox.com/blog/self-hosted-open-source-apps-deployed-with-ox) ox deployed 85 open-source apps from pinned commits on fresh servers, with no Docker. The stacks, services, deploy times and a table of every app. Saurav Sharma Oct 17, 2026 16 min read - Guides [How to self-host AFFiNE on your own VPS without Docker](https://deploywithox.com/blog/self-host-affine) Run AFFiNE v0.27.4 on your own Ubuntu server with ox: Postgres with pgvector, Redis, a Rust and Node build, and an admin made from two variables. Saurav Sharma Oct 17, 2026 12 min read - Guides [How to self-host Akaunting on your own VPS without Docker](https://deploywithox.com/blog/self-host-akaunting) Run Akaunting 3.2.4 on your own Ubuntu server with ox: PHP, MariaDB, a scheduler and the first company and admin made on deploy, from the config that ran. Saurav Sharma Oct 17, 2026 10 min read ## Deploy to a server you own ox sets up systemd, Caddy and PostgreSQL on your Ubuntu server and deploys on every push. Free for your first project. [Sign up with GitHub](https://deploywithox.com/login/github) [Read the docs](https://deploywithox.com/docs)