Installation with Docker
Overview
Docker provides the easiest way to get UnoPim up and running. The Docker setup includes all required services — PHP, Nginx/Apache, MySQL, Redis, Elasticsearch, queue workers, and a task scheduler.
TIP
If you are deploying UnoPim on bare metal (without Docker), see the platform-specific guides instead:
Prerequisites
Make sure the following are installed on your system:
- Docker (v20.10+)
- Docker Compose (v2.0+ — included with Docker Desktop)
Verify installation:
docker --version
docker compose versionQuick Start (Docker Hub)
This is the fastest way to get UnoPim running — no cloning or building required.
curl -O https://raw.githubusercontent.com/unopim/unopim/2.0/docker-compose.hub.yml
curl -O https://raw.githubusercontent.com/unopim/unopim/2.0/.env.docker
cp .env.docker .env
docker compose -f docker-compose.hub.yml up -dWait approximately 90 seconds for the first-time setup (migrations, seeding) to complete, then open:
http://localhost:8000/adminDefault Admin Credentials:
| Field | Value |
|---|---|
admin@example.com | |
| Password | admin123 |
Default Database Credentials (from .env.docker):
| Field | Value |
|---|---|
| Database | unopim |
| Username | unopim |
| Password | password |
| Root Password | password |
WARNING
Change DB_PASSWORD and DB_ROOT_PASSWORD in .env before any non-local deployment.
Development Setup (Build from Source)
This method builds from source and gives full control over the environment. Ideal for development or customization.
Step 1: Clone the Repository
git clone -b 2.0 https://github.com/unopim/unopim.git
cd unopimStep 2: Configure Environment
cp .env.docker .envWARNING
Always copy from .env.docker (not .env.example) — it contains pre-configured Docker service hostnames, ports, and credentials that match the Docker Compose services.
Step 3: Start Containers
Nginx + PHP-FPM (default, recommended for production):
docker compose up -dApache + mod_php (alternative):
docker compose -f docker-compose.yml -f docker-compose.apache.yml up -dThe first start takes a few minutes as Docker builds the images and runs the initial setup (Composer install, key generation, migrations, seeding, Elasticsearch indexing).
Step 4: Access UnoPim
Wait for all containers to become healthy (~90 seconds), then open:
http://localhost:8000/adminLogin with admin@example.com / admin123.
Services Overview
The Docker Compose setup runs the following services:
| Service | Description | Default Host Port |
|---|---|---|
unopim-nginx | Nginx reverse proxy | 8000 |
unopim-fpm | PHP 8.3 FPM application server | (internal: 9000) |
unopim-q | Queue worker (system, completeness, default) | — |
unopim-scheduler | Laravel task scheduler | — |
unopim-mysql | MySQL 8.0 database | 3306 |
unopim-redis | Redis 7.2 (cache + queue) | 6379 |
unopim-elasticsearch | Elasticsearch 8.17 (product search) | 9200 |
unopim-mailpit | Local email testing | 8025 (UI), 1025 (SMTP) |
When using the Apache variant (docker-compose.apache.yml), unopim-nginx and unopim-fpm are replaced by a single unopim-web service.
Bind Address
Only unopim-nginx (or unopim-web in Apache mode) binds to all interfaces (0.0.0.0) so the application is reachable on your LAN. All other ports (MySQL, Redis, Elasticsearch, Mailpit) bind to 127.0.0.1 only and are not exposed outside the host.
Port Configuration
All host-side ports are configurable via FORWARD_* environment variables in your .env file. These only control how ports are mapped to your host machine — they do not affect how services communicate inside Docker.
# Application port
APP_PORT=8000
# Database
FORWARD_DB_PORT=3306
# Redis
FORWARD_REDIS_PORT=6379
# Elasticsearch
FORWARD_ES_PORT=9200
# Mailpit
FORWARD_MAILPIT_PORT=8025
FORWARD_MAILPIT_SMTP_PORT=1025After changing ports, restart the containers:
docker compose down
docker compose up -dRunning Commands Inside Docker
Artisan Commands
docker compose exec unopim-fpm php artisan migrate
docker compose exec unopim-fpm php artisan cache:clear
docker compose exec unopim-fpm php artisan config:cache
docker compose exec unopim-fpm php artisan unopim:product:index
docker compose exec unopim-fpm php artisan queue:restartWARNING
Do not run php artisan queue:work in unopim-fpm. The unopim-q container already runs the queue worker. Use queue:restart to reload workers after a deploy, or queue:failed to inspect failures.
For the Apache variant, replace unopim-fpm with unopim-web:
docker compose exec unopim-web php artisan migrateComposer Commands
docker compose exec unopim-fpm composer install
docker compose exec unopim-fpm composer updateMySQL Access
docker compose exec unopim-mysql mysql -u unopim -p unopimWhen prompted, enter the password from .env (DB_PASSWORD, default password). The trailing unopim selects the database.
Interactive Shell
docker compose exec unopim-fpm bashManaging Containers
# Check status of all services
docker compose ps
# View logs (all services)
docker compose logs -f
# View logs for a specific service
docker compose logs -f unopim-fpm
# Stop all containers
docker compose down
# Stop and remove all data (volumes) — WARNING: destroys database
docker compose down -v
# Rebuild after Dockerfile or dependency changes
docker compose up --build -d
# Restart a single service
docker compose restart unopim-fpmData Persistence
All data is persisted using Docker named volumes:
| Volume | Purpose |
|---|---|
unopim-mysql-data | MySQL database files |
unopim-redis-data | Redis persistence |
unopim-elasticsearch-data | Elasticsearch indices |
WARNING
Running docker compose down -v removes all volumes and permanently deletes your database, search index, and cache. Use docker compose down (without -v) to stop containers while preserving data.
Updating UnoPim
To pull the latest source and rebuild the images:
git pull origin master
docker compose up --build -d
docker compose exec unopim-fpm php artisan migrate
docker compose exec unopim-fpm php artisan unopim:product:index
docker compose exec unopim-fpm php artisan queue:restartFor the Docker Hub setup, pull the new image instead:
docker compose -f docker-compose.hub.yml pull
docker compose -f docker-compose.hub.yml up -dHealth Checks and Logs
Inspect service health and watch container logs:
# Status of every service (look for "(healthy)")
docker compose ps
# Tail logs for a service
docker compose logs -f unopim-fpm
docker compose logs -f unopim-q
docker compose logs -f unopim-nginx
# Application logs inside the container
docker compose exec unopim-fpm tail -f storage/logs/laravel.logA service stuck in (health: starting) for more than two minutes usually indicates a misconfigured .env value or a port conflict — check the logs for that service.
Production Deployment
When deploying UnoPim with Docker in production:
- Change default credentials — Update
DB_PASSWORD,DB_ROOT_PASSWORDin.envbefore first start. - Set
APP_ENV=productionandAPP_DEBUG=falsein.env. - Use a reverse proxy (Nginx, Traefik, or Caddy) for SSL termination.
- Back up volumes regularly — especially the MySQL data volume.
- Use Docker secrets for sensitive credentials instead of
.envin production. See the Docker Compose secrets reference. - Monitor disk space — Elasticsearch requires at least 15% free disk space (see troubleshooting below).
Troubleshooting
Port Conflicts
If you see an error like:
Bind for 127.0.0.1:3306 failed: port is already allocatedThis means another service (e.g., a locally installed MySQL) is already using that port.
Fix: Change the FORWARD_* port in .env:
FORWARD_DB_PORT=33060
FORWARD_REDIS_PORT=16379
FORWARD_ES_PORT=19200
APP_PORT=8080Then restart:
docker compose down
docker compose up -dTo find what is using a specific port:
# Linux
sudo ss -tlnp | grep 3306
# macOS
sudo lsof -i :3306The output reveals whether the holder is a Docker proxy or a host service:
Host service (
mysqld,nginx,apache2,redis-server): stop it with your service manager, e.g.bashsudo systemctl stop mysql sudo systemctl stop nginxOr change the
FORWARD_*port in.envto leave the host service running.Another Docker container: stop it with
bashdocker stop <container-name>
Stale Container Name Conflict
After a failed docker compose up, you may see:
Conflict. The container name "/xxxx_unopim-unopim-scheduler-1" is already in useRemove the orphaned container and retry:
docker compose rm -f
docker compose up -dIf a single container is stuck, remove it directly:
docker rm -f <container-id-or-name>Elasticsearch "503 Service Unavailable" or "no_shard_available"
If the product listing page shows an Elasticsearch error, the cluster may be unhealthy due to disk space. Elasticsearch stops allocating shards when disk usage exceeds 85%.
Check cluster health:
curl http://localhost:9200/_cluster/healthIf status is red and you see unassigned_shards, check disk allocation:
curl http://localhost:9200/_cat/allocation?vTemporary fix — raise the disk watermark thresholds:
curl -X PUT http://localhost:9200/_cluster/settings \
-H 'Content-Type: application/json' \
-d '{
"persistent": {
"cluster.routing.allocation.disk.watermark.low": "95%",
"cluster.routing.allocation.disk.watermark.high": "97%",
"cluster.routing.allocation.disk.watermark.flood_stage": "99%"
}
}'Then reindex products:
docker compose exec unopim-fpm php artisan unopim:product:indexTIP
For production, free up disk space rather than raising watermarks. The defaults (85%/90%/95%) exist to protect your data.
Once disk space is reclaimed, revert the watermark overrides to their defaults:
curl -X PUT http://localhost:9200/_cluster/settings \
-H 'Content-Type: application/json' \
-d '{
"persistent": {
"cluster.routing.allocation.disk.watermark.low": null,
"cluster.routing.allocation.disk.watermark.high": null,
"cluster.routing.allocation.disk.watermark.flood_stage": null
}
}'First-Time Setup Hangs or Fails
The entrypoint script runs migrations and seeds on first start. If it fails:
# Check what happened
docker compose logs unopim-fpm | tail -50
# Retry by removing the lock file and restarting
docker compose exec unopim-fpm rm -f /var/www/html/storage/unopim.lock
docker compose restart unopim-fpmProduct Images Return 404 or 403
Product images are served through a storage symlink: public/storage -> storage/app/public.
# Verify symlink exists
docker compose exec unopim-fpm ls -la public/storage
# If missing, recreate it
docker compose exec unopim-fpm php artisan storage:link
# Fix file permissions
docker compose exec unopim-fpm chown -R www-data:www-data storage bootstrap/cache
docker compose exec unopim-fpm chmod -R 775 storage bootstrap/cachePermission Denied on Log Files
If you see "Failed to open stream: Permission denied" for storage/logs/laravel.log:
docker compose exec unopim-fpm chown -R www-data:www-data storage
docker compose exec unopim-fpm chmod -R 775 storageReset Everything
To completely start fresh:
docker compose down -v # WARNING: removes all data
docker compose up --build -d