Example: Docker Compose deployment
This page illustrates, as an example, an on-premise FoxPlan deployment on a single server with Docker Compose and Traefik as the reverse proxy (TLS termination and automatic Let's Encrypt certificates).
The exact files (docker-compose.yml, scripts, Traefik configuration) are provided with the on-premise deployment package. The excerpts below are given to understand the overall behavior.
Architecture
Internet (DNS: DOMAIN → this server)
│ 80 / 443
▼
┌────────┐ TLS Let's Encrypt (ACME)
│ Traefik│ redirect 80 → 443
└───┬────┘
┌─────────────────┼───────────────────────────┐
▼ ▼ ▼ ▼
/ (app:5000) /docs (docs:8080) /_maintenance (toggle)
product-server docs maintenance-toggle maintenance
│
├──► mongo (internal network)
└──► redis (internal network)
Two variants are provided:
docker-compose.yml— one application instance;docker-compose-scale.yml— several instances, whose load Traefik balances automatically.
Prerequisites
- A Linux server with Docker and Docker Compose v2;
- A domain name whose DNS record points to the server;
- Ports 80 and 443 open from the Internet (mandatory for the ACME challenge);
- Access to the FoxPlan image registry and your license.
Configuration
1. Initial preparation
A script prepares the environment (.env file, certificate storage, maintenance panel credentials):
MAINT_USER=foxops MAINT_PASS='a_strong_password' ./setup-compose.sh
2. Infrastructure variables (.env)
| Variable | Description |
|---|---|
DOMAIN | Public domain served (e.g. app.my-company.com) |
ACME_EMAIL | Let's Encrypt contact email |
APP_IMAGE | Application image to deploy (registry/tag) |
3. Application secrets
Three secret files are created from their .example templates, then filled in:
cp .env_application.example .env_application # app variables (DB, OAuth, S3, JWT…)
cp .env_mongo.example .env_mongo # MongoDB credentials
cp .env_redis.example .env_redis # Redis password
Consistency to respect:
URL_MONGOuses the user/password from.env_mongo, with the hostmongo;SPRING_REDIS_PASSWORDis identical toREDIS_PASSWORD;BASE_URLmatcheshttps://+DOMAIN.
Startup
# Simple variant (1 instance)
docker compose up -d --build
# Scaled variant (several instances)
docker compose -f docker-compose-scale.yml up -d --build
docker compose -f docker-compose-scale.yml up -d --scale product-server=4
On first startup, Traefik automatically obtains the certificate from Let's Encrypt. Verification:
docker compose logs -f traefik # follow certificate issuance
curl -I https://YOUR_DOMAIN/ # should return 200 with a valid certificate
curl -I https://YOUR_DOMAIN/docs/
Maintenance mode
The site can switch to a maintenance page (the /docs documentation remains accessible).
- Via the browser: go to
https://YOUR_DOMAIN/_maintenance(password-protected), then Enable / Restore. - Via the command line:
./compose-maintenance.sh on # enable maintenance
./compose-maintenance.sh off # restore the application
./compose-maintenance.sh status # current state
The toggle is instantaneous and restarts no container (hot reload of Traefik).
TLS certificates (Let's Encrypt)
- Obtained and renewed automatically by Traefik (HTTP-01 challenge);
- No manual action required for renewal.
If a certificate is not issued, check in order:
- The domain resolves to the server (
dig +short YOUR_DOMAIN); - Ports 80 and 443 are open from the Internet;
- The logs:
docker compose logs traefik | grep -i acme.
Routine operations
docker compose ps # services status
docker compose logs -f product-server # application logs
docker compose pull product-server && docker compose up -d # update
docker compose down # stop (keeps data)
MongoDB and Redis data are kept in named volumes, which survive stops/restarts.
Troubleshooting
| Symptom | Lead |
|---|---|
404 on / | Incorrect DOMAIN, or application not started (see logs) |
| 502 / 503 | The application is not listening yet, or a startup error |
| Invalid certificate | DNS, ports 80/443, ACME logs (see above) |
/_maintenance authentication loop | Credentials to regenerate via setup-compose.sh |
| Redis authentication error | SPRING_REDIS_PASSWORD ≠ REDIS_PASSWORD |
| Mongo authentication failure | URL_MONGO inconsistent with .env_mongo |
For the production target with scaling and high availability, see the Kubernetes format.