A batteries-included Docker setup for running Odoo 18 with Postgres, Nginx reverse proxy, Portainer (Docker UI), and pgAdmin. It supports clean addon development with automatic module discovery and Python dependency installation.
- Single
docker-compose.ymlspins up:- Postgres 15 (database)
- Odoo 18 (application)
- Nginx (reverse proxy for Odoo HTTP/longpolling)
- pgAdmin (DB admin UI)
- Portainer CE (Docker management UI)
- Addons workflow:
- Place modules under
addons/custom,addons/enterprise, oraddons/community. - On container restart,
prepare_addons.sh:- Symlinks all discovered modules into
/mnt/extra-addons/auto. - Installs Python dependencies from each module's
requirements.txtorexternal_dependencies['python']in its manifest. - Applies module name priority:
custom>enterprise>community.
- Symlinks all discovered modules into
- Place modules under
- Configurable via
odoo.confandnginx.conf.
[Browser]
|-- HTTP :80 --> [Nginx] --(proxy)--> [Odoo 18]
|--> longpolling :8072 (internal)
[Odoo] <--> [Postgres 15]
Optional UIs:
- Portainer: http://localhost:9000
- pgAdmin: http://localhost:5050
- Odoo (via Nginx): http://localhost/
Logging:
- Log collector service runs continuously and writes to `logs/YYYY-MM-DD/` (per-day folders).
- Per-service files: `db.log`, `odoo.log`, `nginx.log`, plus a combined `all.log`.
- See `logs/README.md` for usage, retention, and customization.
- Docker Engine and Docker Compose plugin installed.
- Ports available: 80 (Nginx), 5432 (Postgres), 5050 (pgAdmin), 9000 (Portainer).
# From repository root
# Start all services in the background
docker compose up -d
# Tail logs (optional)
docker compose logs -f --tail=200Access:
- Odoo: http://localhost/
- Portainer: http://localhost:9000 (set admin password on first run)
- pgAdmin: http://localhost:5050 (login: admin@admin.com / admin)
Note on versions:
- This guide targets Odoo 18. To use Odoo 18, ensure your
docker-compose.ymlusesimage: odoo:18for theodooservice.
-
Odoo (via Nginx)
- URL: http://localhost/
- If remote: replace
localhostwith your server IP or domain. - Odoo master password (database management): defined in
odoo.confasadmin_passwd(default here:Password@123).
-
Portainer (Docker UI)
- URL: http://localhost:9000
- First run prompts you to create an admin user, then manage the local Docker environment.
-
pgAdmin (Postgres UI)
- URL: http://localhost:5050
- Login:
admin@admin.com/admin - Add Server in pgAdmin:
- Name: any (e.g.,
Local Odoo DB) - Connection > Host:
db(Docker service name) - Connection > Port:
5432 - Username:
odoo - Password:
odoo
- Name: any (e.g.,
-
Postgres (direct connection from your host or apps)
- Host:
localhost - Port:
5432 - Username:
odoo - Password:
odoo - Database:
postgres(or your created DB) - Example (psql):
psql postgresql://odoo:odoo@localhost:5432/postgres
- Host:
-
Nginx
- Listens on port 80 and proxies to Odoo.
- To enable HTTPS, configure TLS in
nginx.confand expose 443 (not included by default).
Stop/Restart:
# Stop
docker compose down
# Restart Odoo to rescan addons and install dependencies
docker compose restart odoo- Put module folders in one of:
addons/custom(your org's custom modules)addons/enterprise(licensed Odoo Enterprise modules — not included here)addons/community(OCA or other community modules)
- Each module must contain a
__manifest__.py(or legacy__openerp__.py). - On Odoo container restart:
- All modules are symlinked to
/mnt/extra-addons/auto. - Python dependencies are auto-installed. If OS-level libraries are needed, you may need to extend the Odoo image.
- All modules are symlinked to
Tip: enable Developer Mode in Odoo and install/upgrade modules from Apps.
Key options (pre-filled):
db_host,db_port,db_user,db_password— Postgres connectionaddons_path— includes/mnt/extra-addons/autofirst, thencustom,enterprise,community, and core addons- Worker/performance tuning:
workers,longpolling_portlimit_memory_soft,limit_memory_hardlimit_request,limit_time_cpu,limit_time_real
proxy_mode = True(required when using Nginx)
Defaults in this repo:
admin_passwd = Password@123xmlrpc_port = 8069,xmlrpc_interface = 0.0.0.0workers = 5,longpolling_port = 8072limit_memory_soft = 671088640,limit_memory_hard = 805306368db_maxconn = 64limit_request_filesize = 134217728(128 MB)
Edit and save odoo.conf, then:
docker compose restart odoo- Proxies HTTP to Odoo and handles longpolling.
- To change domains/hosts, update server_name and proxy targets.
- For TLS:
- Basic: terminate TLS at Nginx with your certs and set
proxy_set_header X-Forwarded-Proto https;. - Advanced: integrate with a cert manager/ACME companion (not provided here).
- Basic: terminate TLS at Nginx with your certs and set
After changes:
docker compose restart nginx- Web UI: http://localhost:9000
- First-time setup: create the admin user and manage the local environment (auto-detected when the Docker socket is mounted).
- Security note: mounting
/var/run/docker.sockgives Portainer full control of Docker — restrict access to this port and your host.
- Web UI: http://localhost:5050
- Default credentials are defined in
docker-compose.yml:- email:
admin@admin.com - password:
admin
- email:
- Add a server:
- Host:
db - Port:
5432 - Username:
odoo - Password:
odoo
- Host:
Using pg_dump from the host (optional):
docker compose exec -T db pg_dump -U odoo -d postgres -F c -f /tmp/odoo.backup
docker compose cp db:/tmp/odoo.backup ./odoo.backupRestore:
docker compose cp ./odoo.backup db:/tmp/odoo.backup
docker compose exec -T db pg_restore -U odoo -d postgres --clean --if-exists /tmp/odoo.backup- Pull latest image and recreate the container:
docker compose pull odoo
docker compose up -d odoo- Then update modules from Odoo Apps.
- Path on host:
./logs/YYYY-MM-DD/ - Files per day:
db.log(Postgres),odoo.log(Odoo),nginx.log(Nginx)all.log(combined),collector.log(collector status)
The log-collector service mounts the Docker socket read-only and runs collect_logs.sh every 5 minutes to append logs since midnight, dedupe lines, and maintain these files.
- Tail recent Odoo errors:
grep -i error logs/$(date +%Y-%m-%d)/odoo.log | tail -50
- Check collector status:
tail -50 logs/$(date +%Y-%m-%d)/collector.log - Clean logs older than 30 days:
find logs -maxdepth 1 -type d -name '20*' -mtime +30 -exec rm -rf {} +
See logs/README.md for more examples and how to add more services.
.
├── addons/
│ ├── community/ # Community/OCA modules
│ ├── custom/ # Your custom modules
│ └── enterprise/ # Odoo Enterprise modules (licensed)
├── docker-compose.yml
├── nginx.conf
├── odoo.conf
└── prepare_addons.sh
docker compose up -d- Odoo container starts and runs
prepare_addons.shbefore launching Odoo:- Scans
addons/custom,addons/enterprise,addons/communityfor modules. - Symlinks them into
/mnt/extra-addons/auto. - Reads each module's
requirements.txtand manifestexternal_dependencies['python']to install Python packages automatically.
- Scans
- Odoo boots with
addons_pathpreferring/mnt/extra-addons/auto, so your modules are discovered consistently.
If a module name exists in multiple sources, the first priority wins:
custom > enterprise > community
This is enforced by the script when creating symlinks.
- Change ports if needed (e.g., serve behind an existing reverse proxy): edit
docker-compose.ymlports fornginx,pgadmin, andportainer. - Adjust Odoo worker and memory settings in
odoo.confto match your CPU/RAM. - Use a custom domain and TLS by editing
nginx.confand adding your certificates. - If modules require OS-level libs (e.g.,
libxml2-dev,build-essential), create a small Dockerfile that extendsodoo:18and installs them, then update theodooservice to build from your Dockerfile.
- Missing Python package: ensure it's declared in
requirements.txtorexternal_dependencies['python']; restart Odoo to auto-install. - Module not detected: check it contains
__manifest__.py; verify symlink in/mnt/extra-addons/autoinside the container. - Nginx 502: check that
odoois healthy andnginx.confupstreams match exposed ports. - Permission issues on Linux: ensure your user can run Docker and that bind-mounted paths are accessible.
Happy building! If you want, you can enable HTTPS and a public hostname; I can help wire that up with Nginx and certificates.