Skip to content

Latest commit

 

History

History
120 lines (88 loc) · 3.53 KB

File metadata and controls

120 lines (88 loc) · 3.53 KB

odoo-infra

Production-ready Docker Compose stack for Odoo 18. Built as part of the much. Consulting SRE challenge.

Includes Nginx reverse proxy, PostgreSQL 18, automated backups, and metrics exporters for centralised observability.

Stack

  • Odoo 18 + PostgreSQL 18 (Alpine)
  • Nginx 1.30 — TLS termination, security headers, rate limiting
  • Backup container — daily pg_dump + filestore archive, 30-day retention
  • node-exporter + postgres-exporter — metrics for central Prometheus scraping

Quick Start

# 1. Clone the repo
git clone <repo-url> && cd odoo-infra

# 2. Generate secrets
mkdir -p secrets
openssl rand -base64 32 > secrets/pg_password.txt
openssl rand -base64 32 > secrets/odoo_admin_passwd.txt
chmod 600 secrets/*.txt

# ⚠️ After generating secrets, update the password values manually in:
# - docker-compose.yml (POSTGRES_PASSWORD, PASSWORD, DATA_SOURCE_NAME)
# - config/odoo.conf (db_password)
# See secrets/README.md for details

mkdir -p config/nginx/ssl
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout config/nginx/ssl/server.key \
  -out config/nginx/ssl/server.crt \
  -subj "/CN=localhost"

# 4. Bring up the stack
docker compose up -d

First Boot — Database Initialisation

Odoo requires a one-time manual database initialisation on first run. The official image does not auto-initialise when using a custom odoo.conf.

# Shell into a temporary Odoo container
docker compose run --rm odoo bash

# Run initialisation (takes 2-3 minutes)
/usr/bin/odoo -i base \
  --db_host=db \
  --db_user=odoo \
  --db_password=odoo \
  --database=odoo \
  --without-demo=all \
  --stop-after-init

# Exit and bring stack up normally
exit
docker compose up -d

This is only needed once. Data persists in the pg_data volume across all restarts. Only docker compose down -v would require re-initialisation.

Credentials

For this deployment credentials are set as plain environment variables in docker-compose.yml. This is intentional for local/demo use.

Production path: inject via CI/CD from HashiCorp Vault or AWS Secrets Manager at deploy time — never stored in the image or repository.

Note: Docker native secrets (POSTGRES_PASSWORD_FILE) work for PostgreSQL but Odoo 18 has no native _FILE env var support. A custom entrypoint wrapper was evaluated and rejected due to fragility.

Local Observability (optional)

A separate docker-compose.monitoring.yml runs Prometheus + Grafana locally, communicating with the main stack via a shared Docker network.

docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d

Grafana → http://localhost:3000 (admin / admin)
Prometheus → http://localhost:9090

For production with a central Prometheus server, uncomment the ports: lines in the exporter services and replace 100.x.x.x with your VPN IP.

Services

Service Access Notes
Odoo https://localhost Main application
Nginx :80 / :443 Reverse proxy
PostgreSQL internal only Not published to host
node-exporter MONITORING_IP:9100 Host metrics
postgres-exporter MONITORING_IP:9187 DB metrics

Backup & Restore

Backups run daily at 02:00 UTC. Files stored in the backup_data volume.

# Trigger manual backup
docker exec odoo-backup /bin/sh /backup.sh

# Restore database
docker exec -i odoo-db psql -U odoo odoo < backup_YYYY-MM-DD.sql

# Restore filestore
docker run --rm \
  -v odoo_data:/var/lib/odoo \
  -v $(pwd)/backups:/backups alpine \
  tar -xzf /backups/filestore_YYYY-MM-DD.tar.gz -C /var/lib/odoo