Install with Docker
Inventoros publishes a production Docker image to the GitHub Container Registry for every release:
ghcr.io/inventoros/inventoros:latest # newest release
ghcr.io/inventoros/inventoros:1.2.3 # an exact release
ghcr.io/inventoros/inventoros:1.2 # newest patch of a minor line
The image runs PHP 8.4 under FrankenPHP (Caddy and PHP in one process), serves the app on port 8080 as a non-root user, and ships with production settings: APP_ENV=production, APP_DEBUG=false, OPcache on, and config, route, view and event caches built at start. The mysqldump, pg_dump and sqlite3 clients are included so the built-in backups can capture the database.
Prerequisites
- Docker 24.0 or higher
- Docker Compose v2
- At least 1GB RAM and 5GB disk space
Quick start with Docker Compose
The repository contains a ready-to-use docker-compose.prod.yml that runs four containers: the web app, a queue worker, the scheduler, and PostgreSQL 17.
- Download the compose file into an empty directory:
mkdir inventoros && cd inventoros
curl -fsSLO https://raw.githubusercontent.com/Inventoros/Inventoros/main/docker-compose.prod.yml
- Create a
.envfile next to it:
cat > .env <<EOF
APP_KEY=base64:$(openssl rand -base64 32)
APP_URL=https://inventory.example.com
DB_PASSWORD=$(openssl rand -hex 24)
EOF
Keep this file safe. APP_KEY encrypts sessions and stored secrets, so changing it later logs everyone out and makes encrypted values unreadable.
- Start the stack:
docker compose -f docker-compose.prod.yml up -d
- Open
APP_URL/install(or http://localhost:8080/install) and complete the web installer. On the database step enter hostdb, port5432, databaseinventoros, userinventorosand theDB_PASSWORDfrom your.env.
What runs where
| Service | Command | Notes |
|---|---|---|
app |
FrankenPHP web server | Runs migrations on start (RUN_MIGRATIONS=true), exposes port 8080, health check on /up |
worker |
php artisan queue:work |
Sends queued mail, webhooks, imports and exports |
scheduler |
php artisan schedule:work |
Reorder-point checks, retention pruning and other scheduled jobs |
db |
PostgreSQL 17 | Data in the db-data volume |
Uploads, logs and backups live in /app/storage, which is the storage volume shared by the three Inventoros containers. Installed plugin code lives in the shared plugins volume (/app/plugins) and published plugin UI assets in plugin-assets (/app/public/plugin-assets). Back up these three volumes and db-data. Keeping plugin code shared lets queued jobs and the scheduler use the same plugins as the web app, and preserves installations when containers are recreated.
After installing, updating, activating or deactivating a plugin, restart the worker and scheduler so their long-running processes load the current plugin code:
docker compose -f docker-compose.prod.yml restart worker scheduler
If you previously ran the compose file without plugin volumes, copy /app/plugins and /app/public/plugin-assets out of the existing app container before recreating it, then restore them into the new volumes. Existing container files are not automatically migrated into a new named volume.
Configuration
Every Laravel setting can be passed as an environment variable. The ones specific to the image:
| Variable | Default | Purpose |
|---|---|---|
APP_KEY |
required | Encryption key. The container refuses to start without it |
RUN_MIGRATIONS |
false |
Run php artisan migrate --force before starting. Enable it on one service only |
CACHE_ON_START |
true |
Build config, route, view and event caches at start |
SERVER_NAME |
:8080 |
Caddy site address. Set to a domain to let Caddy obtain HTTPS certificates itself (then publish ports 80 and 443) |
DB_WAIT_SECONDS |
60 |
How long to wait for the database port before starting |
SESSION_SECURE_COOKIE |
true |
Set to false only if you serve the app over plain HTTP |
To use MySQL instead of PostgreSQL, replace the db service with a mysql:8.0 container and set DB_CONNECTION=mysql, DB_PORT=3306 in the shared environment block.
HTTPS
By default the container speaks plain HTTP on port 8080, which suits a reverse proxy or load balancer that terminates TLS (Traefik, nginx, Caddy, a cloud load balancer). To let the container handle TLS on its own, set SERVER_NAME=inventory.example.com and publish ports 80:80 and 443:443 on the app service.
Updating
Pull the new image and recreate the containers. The app container applies migrations on start:
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
Pin a version with INVENTOROS_IMAGE=ghcr.io/inventoros/inventoros:1.2.3 in .env if you prefer to upgrade deliberately. Do not use the in-app updater inside a container: it replaces files in the running container, and those changes are lost when the container is recreated.
Building the image yourself
git clone https://github.com/Inventoros/Inventoros.git
cd Inventoros
docker build -t inventoros .
docker compose -f docker-compose.prod.yml up -d --build builds from the checkout instead of pulling from GHCR.
Development stack
docker-compose.yml in the repository is a development environment, not a deployment: it bind-mounts the source, enables APP_DEBUG, uses SQLite and includes Mailpit. Start it with docker compose up --build and see docker/README.md for details.
Report an issue: https://github.com/Inventoros/Inventoros/issues
Need a hand?
Open an issue or start a discussion on GitHub and the community will help you out.