@@ -19,25 +19,50 @@ See [Production Deployment](#production-deployment) below.
1919
2020## Files
2121
22- | File | Description |
23- | ------------------------------- | ------------------------------------------------- |
24- | ` Dockerfile ` | Multi-stage build for OpenSPP |
25- | ` docker-compose.production.yml ` | Production stack (Traefik + Odoo + Queue Worker) |
26- | ` .env.production.example ` | Production configuration template |
27- | ` entrypoint.sh ` | Container entrypoint with database initialization |
28- | ` odoo.conf.template ` | Odoo configuration template |
29- | ` requirements.txt ` | Python dependencies beyond module manifests |
30- | ` requirements-dev.txt ` | Development-only dependencies |
22+ | File | Description |
23+ | ------------------------------------ | ------------------------------------------------- |
24+ | ` Dockerfile ` | Multi-stage build for OpenSPP |
25+ | ` docker-compose.production.yml ` | Production stack (Traefik + Odoo + Queue Worker) |
26+ | ` docker-compose.nginx.yml ` | Production stack (Nginx + Certbot + Fluentd) |
27+ | ` .env.production.example ` | Production configuration template |
28+ | ` entrypoint.sh ` | Container entrypoint with database initialization |
29+ | ` odoo.conf.template ` | Odoo configuration template |
30+ | ` nginx/odoo.conf.template ` | Nginx HTTPS reverse proxy config (Odoo docs) |
31+ | ` nginx/odoo-http-only.conf.template ` | Nginx HTTP-only reverse proxy config |
32+ | ` nginx/certbot-init.sh ` | Bootstrap script for initial SSL certificate |
33+ | ` fluentd/ ` | Fluentd log collection configs |
34+ | ` backup.sh ` | Automated PostgreSQL/PostGIS backup script |
35+ | ` backup-entrypoint.sh ` | Backup container entrypoint with cron scheduling |
36+ | ` requirements.txt ` | Python dependencies beyond module manifests |
37+ | ` requirements-dev.txt ` | Development-only dependencies |
3138
3239## Production Deployment
3340
34- ### Architecture
41+ Two production compose files are available:
42+
43+ | Compose file | Reverse proxy | SSL | Logging |
44+ | ------------------------------- | ------------- | ------------------------- | -------------- |
45+ | ` docker-compose.production.yml ` | Traefik | Let's Encrypt (automatic) | Docker default |
46+ | ` docker-compose.nginx.yml ` | Nginx | Certbot (Let's Encrypt) | Fluentd |
47+
48+ ### Architecture (Traefik)
3549
3650```
3751Internet -> Traefik (SSL) -> Odoo (workers) -> PostgreSQL
3852 -> Queue Worker
3953```
4054
55+ ### Architecture (Nginx)
56+
57+ ```
58+ Internet -> Nginx (SSL) -> Odoo (workers) -> PostgreSQL
59+ -> Queue Worker
60+ Certbot (certificate renewal)
61+ Fluentd (log collection -> local/NFS, S3, GCS, Azure, MinIO)
62+ ClamAV (antivirus, optional)
63+ Backup (daily PostgreSQL dumps)
64+ ```
65+
4166### Requirements
4267
4368- VPS with Docker and Docker Compose v2
@@ -73,15 +98,106 @@ ODOO_ADMIN_PASSWD=<strong-random-password>
73983 . ** Start services:**
7499
75100``` bash
101+ # Traefik stack
76102docker compose -f docker/docker-compose.production.yml up -d
103+
104+ # Nginx stack (HTTPS)
105+ docker compose -f docker/docker-compose.nginx.yml --profile https up -d
77106```
78107
791084 . ** View logs:**
80109
81110``` bash
82111docker compose -f docker/docker-compose.production.yml logs -f odoo
112+ # or
113+ docker compose -f docker/docker-compose.nginx.yml logs -f odoo
114+ ```
115+
116+ ### Nginx Profiles
117+
118+ The Nginx compose file uses profiles to select the deployment mode:
119+
120+ | Profile | Services added | Use case |
121+ | -------- | --------------------- | -------------------------------------- |
122+ | ` https ` | Nginx (SSL) + Certbot | Production with HTTPS and auto-renewal |
123+ | ` http ` | Nginx (HTTP only) | Dev or isolated on-premises networks |
124+ | ` clamav ` | ClamAV antivirus | File upload scanning (~ 1GB extra RAM) |
125+
126+ Profiles can be combined:
127+
128+ ``` bash
129+ # HTTPS + ClamAV
130+ docker compose -f docker/docker-compose.nginx.yml --profile https --profile clamav up -d
131+
132+ # Or set in .env.production (no --profile flags needed):
133+ COMPOSE_PROFILES=https,clamav
134+ ```
135+
136+ HTTP-only mode (no SSL, no Certbot):
137+
138+ ``` bash
139+ docker compose -f docker/docker-compose.nginx.yml --profile http up -d
140+ ```
141+
142+ ### Nginx SSL Certificate Setup
143+
144+ 1 . ** Obtain initial certificate** (HTTPS profile only):
145+
146+ ``` bash
147+ docker compose -f docker/docker-compose.nginx.yml run --rm certbot \
148+ certonly --webroot -w /var/www/certbot \
149+ --email ${ACME_EMAIL} -d ${DOMAIN} --agree-tos --non-interactive
150+
151+ # Reload nginx to pick up the new certificate
152+ docker compose -f docker/docker-compose.nginx.yml exec nginx nginx -s reload
83153```
84154
155+ 2 . ** Schedule renewal** via system cron (certificates expire every 90 days):
156+
157+ ``` bash
158+ # Add to system crontab (runs monthly)
159+ 0 3 1 * * cd /path/to/your/project && docker compose -f docker/docker-compose.nginx.yml run --rm certbot renew && docker compose -f docker/docker-compose.nginx.yml exec nginx nginx -s reload
160+ ```
161+
162+ ### Nginx Security Hardening
163+
164+ All services in the Nginx compose file include:
165+
166+ - ** Capability dropping:** ` cap_drop: ALL ` with minimal ` cap_add ` per service
167+ - ** No privilege escalation:** ` security_opt: no-new-privileges:true `
168+ - ** Read-only filesystems:** ` read_only: true ` with targeted tmpfs mounts (where
169+ possible)
170+ - ** Resource limits:** CPU and memory limits on every service
171+ - ** Log rotation:** ` json-file ` driver with 5MB/3-file rotation (15MB max per service)
172+ - ** SELinux compatibility:** ` :ro,z ` and ` :rw,z ` volume flags
173+
174+ Nginx adds: rate limiting, security headers (HSTS, CSP, X-Frame-Options), OCSP stapling,
175+ proxy buffering, ` /web/database ` blocking.
176+
177+ ### Centralized Logging with Fluentd (Nginx stack)
178+
179+ The Nginx compose file includes Fluentd for centralized log collection. It uses file
180+ tailing (not Docker's log driver), so ` docker compose logs ` keeps working and logs are
181+ never lost if Fluentd goes down.
182+
183+ ** Default backend:** Local filesystem / NFS (` output-file.conf ` )
184+
185+ ** Available backends:**
186+
187+ | Backend | Config file | Plugins required |
188+ | ---------------- | ---------------------------- | --------------------------------------- |
189+ | Local / NFS | ` output-file.conf ` (default) | None (built-in) |
190+ | AWS S3 | ` output-s3.conf.example ` | fluent-plugin-s3 |
191+ | Google Cloud GCS | ` output-gcs.conf.example ` | fluent-plugin-gcs |
192+ | Azure Blob | ` output-azure.conf.example ` | fluent-plugin-azure-storage-append-blob |
193+ | MinIO | ` output-minio.conf.example ` | fluent-plugin-s3 |
194+
195+ To switch backends:
196+
197+ 1 . Edit ` docker/fluentd/fluent.conf ` to ` @include ` the desired output config
198+ 2 . For cloud backends, uncomment the ` build: ` block in the fluentd service to install
199+ plugins
200+
85201### Using External Database (RDS/Cloud SQL)
86202
872031 . Set ` DATABASE_URL ` in ` .env.production ` :
@@ -90,7 +206,7 @@ docker compose -f docker/docker-compose.production.yml logs -f odoo
90206DATABASE_URL=postgres://user:password@hostname:5432/openspp? sslmode=require
91207```
92208
93- 2 . Comment out or remove the ` db ` service in ` docker- compose.production.yml `
209+ 2 . Comment out or remove the ` db ` service in the compose file
94210
952113 . Update ` depends_on ` in ` odoo ` and ` queue-worker ` services
96212
@@ -140,8 +256,11 @@ ClamAV antivirus scanning is available as an optional profile. Enable it when:
140256** Enable ClamAV:**
141257
142258``` bash
143- # Start with ClamAV profile
259+ # Traefik stack
144260docker compose -f docker/docker-compose.production.yml --profile clamav up -d
261+
262+ # Nginx stack
263+ docker compose -f docker/docker-compose.nginx.yml --profile https --profile clamav up -d
145264```
146265
147266** Configure Odoo:**
@@ -172,6 +291,7 @@ docker compose -f docker/docker-compose.production.yml exec clamav clamscan --ve
172291
173292| Variable | Description |
174293| ------------------- | ------------------------------------- |
294+ | ` COMPOSE_PROFILES ` | Deployment profile (Nginx stack only) |
175295| ` DB_PASSWORD ` | PostgreSQL password |
176296| ` ODOO_ADMIN_PASSWD ` | Odoo admin/master password |
177297| ` DOMAIN ` | Domain name (production only) |
@@ -199,6 +319,20 @@ docker compose -f docker/docker-compose.production.yml exec clamav clamscan --ve
199319| ` ODOO_TIME_CPU ` | 600 | CPU time limit per request (seconds) |
200320| ` ODOO_TIME_REAL ` | 1200 | Real time limit per request (seconds) |
201321
322+ ### Resource Limits (Nginx stack)
323+
324+ | Variable | Default | Description |
325+ | --------------------- | ------- | ------------------------- |
326+ | ` ODOO_CPU_LIMIT ` | 2 | Odoo CPU cores |
327+ | ` ODOO_MEMORY_LIMIT ` | 4G | Odoo memory limit |
328+ | ` DB_CPU_LIMIT ` | 2 | PostgreSQL CPU cores |
329+ | ` DB_MEMORY_LIMIT ` | 4G | PostgreSQL memory limit |
330+ | ` QUEUE_CPU_LIMIT ` | 1 | Queue worker CPU cores |
331+ | ` QUEUE_MEMORY_LIMIT ` | 2G | Queue worker memory limit |
332+ | ` NGINX_CPU_LIMIT ` | 1 | Nginx CPU cores |
333+ | ` NGINX_MEMORY_LIMIT ` | 256M | Nginx memory limit |
334+ | ` CLAMAV_MEMORY_LIMIT ` | 1536M | ClamAV memory limit |
335+
202336### Logging
203337
204338| Variable | Default | Description |
0 commit comments