Skip to Content

A Practical Docker Compose Topology for Odoo and Astro

Private services, automatic TLS, health checks, persistent volumes, and a clean path to backup and restore.
September 23, 2026 by
A Practical Docker Compose Topology for Odoo and Astro

A small production stack is easier to reason about when every service has one job and only the reverse proxy is exposed to the Internet.

Separate concerns

  • Astro renders the public portfolio and blog.
  • Odoo provides editorial workflows.
  • PostgreSQL owns durable relational state.
  • Caddy terminates TLS and routes public hostnames.

Odoo and PostgreSQL remain on an internal Docker network. Astro can call Odoo, but neither service needs a public port.

Health before dependency

Waiting for a process to start is not the same as waiting for it to become useful. Define service health around the capability the next service depends on.

services:
  odoo:
    healthcheck:
      test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8069/web/version')"]
  astro:
    depends_on:
      odoo:
        condition: service_healthy

Persist the right data

A database dump is not a complete Odoo backup. Binary fields and uploaded images can live in the filestore, so back up both PostgreSQL and /var/lib/odoo.

Keep build-time and runtime configuration separate

Public site URLs may be compiled into an Astro build. Private integration URLs should remain runtime environment variables. This distinction prevents a container from silently falling back to localhost after deployment.

Automate TLS, then verify it

Caddy can obtain and renew certificates automatically. Still verify the certificate, public DNS, HTTP redirects, login routes, and service health after each deployment.

A successful container start is only the beginning of deployment verification.

What I Learned Structuring an Odoo 19 Custom Addon
Models, access rules, editor UX, and a read-only delivery endpoint are one system—not four unrelated files.