跳轉至

建立 2026-09-14 更新 2026-09-14

Docker 部署

只想先看到登入畫面,用 安裝與環境 的最小 Compose 即可。本頁給兩套可進版控的堆疊:開發(含測信與備份)與正式(HTTPS)。同一套目錄、依客戶改 .env,避免每台機器從零拼容器。

  • 開發(dev):Odoo 18、PostgreSQL、MailCatcher、每日資料庫備份,可選 VS Code Dev Container。
  • 正式(prod):再加 Nginx 反向代理與 Let’s Encrypt。密碼與網域只放 .env

目錄結構(建議)

odoo-stack/
├─ dev/                      # 本機開發測試
│  ├─ compose-dev.yml
│  ├─ .env.example
│  ├─ odoo/
│  │  ├─ config/odoo.conf
│  │  ├─ extra-addons/
│  │  └─ filestore/
│  ├─ postgres/
│  └─ backups/
├─ prod/                     # 正式上線(客戶機器)
│  ├─ compose-prod.yml
│  ├─ .env.example
│  ├─ nginx/
│  │  ├─ certs/             # acme-companion 自動管理
│  │  ├─ html/              # ACME 驗證 webroot
│  │  ├─ vhost.d/           # 針對單一網域覆寫
│  │  └─ conf.d/00-tuning.conf
│  ├─ odoo/
│  │  ├─ config/odoo.conf
│  │  ├─ extra-addons/
│  │  └─ filestore/
│  ├─ postgres/
│  └─ backups/
└─ .devcontainer/            # 可選:VS Code Remote Containers(對 dev/ 使用)
   └─ devcontainer.json

本機開發與測試(dev)

1) dev/.env.example(複製成 dev/.env 後再修改)

# 基本
STACK_NAME=odoo18-dev
ODOO_VERSION=18.0

# Postgres
POSTGRES_DB=odoodb
POSTGRES_USER=odoo
POSTGRES_PASSWORD=dev_supersecret
PGDATA=/var/lib/postgresql/data/pgdata

# 備份
BACKUP_SCHEDULE=0 30 3 * * *     # 每天 03:30
BACKUP_KEEP_DAYS=7
BACKUP_COMPRESS=true

# MailCatcher(Odoo 發信測試)
MAILCATCHER_HTTP_PORT=1080
MAILCATCHER_SMTP_PORT=1025

# 對外映射(本機使用)
ODOO_WEB_PORT=8069

2) dev/odoo/config/odoo.conf

[options]
; addons 路徑:官方 + 你的自訂模組
addons_path = /usr/lib/python3/dist-packages/odoo/addons,/mnt/extra-addons
data_dir = /var/lib/odoo

; 重要:開 dev/proxy 模式
proxy_mode = True

; DB 連線(與 compose 一致)
db_host = db
db_port = 5432
db_user = odoo
db_password = dev_supersecret

; 資源限制(本機可酌情放寬)
limit_time_cpu = 120
limit_time_real = 120

; E-mail(指向 MailCatcher 的 SMTP)
smtp_server = mailcatcher
smtp_port = 1025
smtp_ssl = False

3) dev/compose-dev.yml

version: "3.9"

services:
  db:
    image: postgres:16
    container_name: ${STACK_NAME}-db
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - PGDATA=${PGDATA}
    volumes:
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
      interval: 10s
      timeout: 5s
      retries: 10
    restart: unless-stopped

  odoo:
    image: odoo:${ODOO_VERSION}
    container_name: ${STACK_NAME}-odoo
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "${ODOO_WEB_PORT}:8069"
    environment:
      - TZ=Asia/Taipei
    volumes:
      - ./odoo/filestore:/var/lib/odoo
      - ./odoo/extra-addons:/mnt/extra-addons
      - ./odoo/config/odoo.conf:/etc/odoo/odoo.conf:ro
    restart: unless-stopped

  # 測試郵件:Web UI http://localhost:1080 ,SMTP host=mailcatcher port=1025
  mailcatcher:
    image: dockage/mailcatcher:0.9.0
    container_name: ${STACK_NAME}-mailcatcher
    ports:
      - "${MAILCATCHER_HTTP_PORT}:1080"
      - "${MAILCATCHER_SMTP_PORT}:1025"
    restart: unless-stopped

  # 每日自動備份(輸出到 ./backups)
  pgbackups:
    image: prodrigestivill/postgres-backup-local:16
    container_name: ${STACK_NAME}-pgbackups
    environment:
      - POSTGRES_HOST=db
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - SCHEDULE=${BACKUP_SCHEDULE}
      - BACKUP_KEEP_DAYS=${BACKUP_KEEP_DAYS}
      - COMPRESS=${BACKUP_COMPRESS}
      - BACKUP_DIR=/backups
    volumes:
      - ./backups:/backups
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

4) 一鍵啟動(dev)

cd odoo-stack/dev
cp .env.example .env      # 修改成你的參數
docker compose -f compose-dev.yml up -d

5) (可選)VS Code Remote Containers

/.devcontainer/devcontainer.json

{
  "name": "Odoo 18 Dev",
  "dockerComposeFile": ["../dev/compose-dev.yml"],
  "service": "odoo",
  "workspaceFolder": "/workspace",
  "shutdownAction": "stopCompose",
  "postCreateCommand": "pip3 install --user black isort",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "tamasfe.even-better-toml",
        "oderwat.indent-rainbow",
        "batisteo.vscode-django"
      ],
      "settings": {
        "python.defaultInterpreterPath": "/usr/bin/python3",
        "python.analysis.typeCheckingMode": "basic",
        "editor.formatOnSave": true
      }
    }
  },
  "remoteUser": "root",
  "mounts": [
    "source=${localWorkspaceFolder}/dev/odoo/extra-addons,target=/mnt/extra-addons,type=bind",
    "source=${localWorkspaceFolder},target=/workspace,type=bind"
  ]
}

打開 VS Code → Dev Containers: Reopen in Container,就能在 Odoo 容器內開發,/mnt/extra-addons 放你的自製模組。


正式上線(prod)

1) prod/.env.example(複製成 prod/.env 後再修改)

# 堆疊與版本
STACK_NAME=odoo18
ODOO_VERSION=18.0

# 你的正式網域(A/AAAA 必須指向這台機器)
ODOO_DOMAIN=odoo.example.com
LETSENCRYPT_EMAIL=admin@example.com

# Postgres
POSTGRES_DB=odoodb
POSTGRES_USER=odoo
POSTGRES_PASSWORD=change_this_prod_secret
PGDATA=/var/lib/postgresql/data/pgdata

# 備份
BACKUP_SCHEDULE=0 15 3 * * *     # 每天 03:15
BACKUP_KEEP_DAYS=14
BACKUP_COMPRESS=true

2) prod/odoo/config/odoo.conf

[options]
addons_path = /usr/lib/python3/dist-packages/odoo/addons,/mnt/extra-addons
data_dir = /var/lib/odoo
proxy_mode = True

db_host = db
db_port = 5432
db_user = odoo
db_password = change_this_prod_secret

limit_time_cpu = 120
limit_time_real = 120

; 上線環境的郵件請設你/客戶的 SMTP(這裡示意留白)
; smtp_server = smtp.example.com
; smtp_port = 587
; smtp_ssl = False
; smtp_user = ...
; smtp_password = ...

3) prod/nginx/conf.d/00-tuning.conf

client_max_body_size 64m;

proxy_read_timeout 600s;
proxy_send_timeout 600s;
send_timeout 600s;

gzip on;
gzip_types text/plain text/css application/json application/javascript application/xml;

4) (可選)針對該網域的覆寫 prod/nginx/vhost.d/odoo.example.com

檔名請改成你的真實網域

proxy_buffering off;

# WebSocket
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header Referrer-Policy strict-origin-when-cross-origin;

# 強制 HTTPS(評估影響再開)
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

5) prod/compose-prod.yml

version: "3.9"

services:
  nginx-proxy:
    image: nginxproxy/nginx-proxy:alpine
    container_name: ${STACK_NAME}-nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/certs:/etc/nginx/certs:ro
      - ./nginx/vhost.d:/etc/nginx/vhost.d
      - ./nginx/html:/usr/share/nginx/html
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - /var/run/docker.sock:/tmp/docker.sock:ro
    restart: unless-stopped

  acme-companion:
    image: nginxproxy/acme-companion
    container_name: ${STACK_NAME}-acme
    environment:
      - DEFAULT_EMAIL=${LETSENCRYPT_EMAIL}
      - NGINX_PROXY_CONTAINER=${STACK_NAME}-nginx-proxy
    volumes:
      - ./nginx/certs:/etc/nginx/certs
      - ./nginx/vhost.d:/etc/nginx/vhost.d
      - ./nginx/html:/usr/share/nginx/html
      - /var/run/docker.sock:/var/run/docker.sock:ro
    depends_on:
      - nginx-proxy
    restart: unless-stopped

  db:
    image: postgres:16
    container_name: ${STACK_NAME}-db
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - PGDATA=${PGDATA}
    volumes:
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
      interval: 10s
      timeout: 5s
      retries: 10
    restart: unless-stopped

  odoo:
    image: odoo:${ODOO_VERSION}
    container_name: ${STACK_NAME}-odoo
    depends_on:
      db:
        condition: service_healthy
      nginx-proxy:
        condition: service_started
      acme-companion:
        condition: service_started
    environment:
      - TZ=Asia/Taipei
      # 讓 nginx-proxy 反代 + acme 簽憑證
      - VIRTUAL_HOST=${ODOO_DOMAIN}
      - LETSENCRYPT_HOST=${ODOO_DOMAIN}
      - VIRTUAL_PORT=8069
    expose:
      - "8069"
    volumes:
      - ./odoo/filestore:/var/lib/odoo
      - ./odoo/extra-addons:/mnt/extra-addons
      - ./odoo/config/odoo.conf:/etc/odoo/odoo.conf:ro
    restart: unless-stopped

  pgbackups:
    image: prodrigestivill/postgres-backup-local:16
    container_name: ${STACK_NAME}-pgbackups
    environment:
      - POSTGRES_HOST=db
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - SCHEDULE=${BACKUP_SCHEDULE}
      - BACKUP_KEEP_DAYS=${BACKUP_KEEP_DAYS}
      - COMPRESS=${BACKUP_COMPRESS}
      - BACKUP_DIR=/backups
    volumes:
      - ./backups:/backups
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

6) 一鍵上線(prod)

  1. DNS 指向:把 ODOO_DOMAIN(例如 odoo.example.com)的 A/AAAA 記錄指到該伺服器。
  2. 建立資料夾(照上面目錄),把 .env.example 複製成 .env 並修改參數。
  3. 啟動:

cd odoo-stack/prod
cp .env.example .env
docker compose -f compose-prod.yml up -d
4. 幾分鐘內會自動簽到憑證,直接開 https://你的網域


設定說明(重點參數)

  • ODOO_VERSION=18.0:指定 Odoo 版本 18。
  • ODOO_DOMAIN(prod):要對外服務的網域名稱;務必先設定好 DNS
  • LETSENCRYPT_EMAIL(prod):憑證註冊/續期通知信箱。
  • POSTGRES_*:資料庫名稱、使用者、密碼;dev 與 prod 建議不同
  • BACKUP_*:備份排程、保留天數、壓縮。備份輸出在 ./backups/
  • PGDATA:PostgreSQL 的內部資料目錄(保持預設即可)。
  • VIRTUAL_HOST / LETSENCRYPT_HOST(prod):由 nginx-proxy / acme-companion 使用,請設定為你的 ODOO_DOMAIN

常見情境與排錯(nginx / HTTPS)

  • 80/443 未開或被防火牆擋住 → 憑證簽發會失敗。請放行 TCP 80、443。
  • DNS 尚未生效 → 憑證挑戰過不了;稍等或確認 A/AAAA 是否正確。
  • 附件上傳 413 → 調大 client_max_body_size(已預設 64m)。
  • 報表或長查詢逾時 → 調整 proxy_read_timeout / send_timeout(預設 600 秒)。
  • HSTS 啟用 → 改 vhost.d/<domain> 檔加入 HSTS 標頭,注意影響子網域與測試環境。
  • 多客戶/多站 → 同一台機器可以再加一個 Odoo 服務,只要給不同的 VIRTUAL_HOST/LETSENCRYPT_HOST 與資料、備份目錄即可。

備份與還原

自動備份

  • 每日產出檔案於 dev/backups/prod/backups/
  • 你可再用 restic / rclone / rsync 同步到 S3 / Backblaze / NAS 異地備援。

手動備份

# 在 dev 或 prod 目錄下
docker exec -it ${STACK_NAME}-db pg_dump -U ${POSTGRES_USER} -d ${POSTGRES_DB} > backups/manual.sql

還原(建議停 Odoo 再進行)

docker stop ${STACK_NAME}-odoo

# 砍 DB、重建(或用新 DB 名)
docker exec -it ${STACK_NAME}-db psql -U ${POSTGRES_USER} -d postgres -c "DROP DATABASE ${POSTGRES_DB};"
docker exec -it ${STACK_NAME}-db psql -U ${POSTGRES_USER} -d postgres -c "CREATE DATABASE ${POSTGRES_DB} OWNER ${POSTGRES_USER};"

# 匯入 .sql.gz
gunzip -c backups/odoodb_YYYYMMDD_HHMMSS.sql.gz | docker exec -i ${STACK_NAME}-db psql -U ${POSTGRES_USER} -d ${POSTGRES_DB}

docker start ${STACK_NAME}-odoo

可以直接拿這包幫客戶上線並管理嗎?

可以,且非常適合。建議再加:

  • 系統監控:cAdvisor + Prometheus + Grafana(CPU/RAM/磁碟/網路)。
  • 日誌集中:用 Loki/ELK 收集 nginxodoo 日誌。
  • 安全性:只開 80/443/SSH;Postgres 僅限容器網路;定期更新容器版本。
  • 異地備援:備份同步到 S3/NAS;定期做「備份還原演練」。

同一機要跑多套 Odoo 時,請給不同的 VIRTUAL_HOST、資料目錄與備份目錄,不要共用同一個 Postgres volume。資料庫 GUI 與 Ansible 自動化不在本頁範圍;本機看表可用 完整堆疊 裡的 pgAdmin。

正式寄信不要用 MailCatcher,改 郵件設定

進一步學習