Cài đặt Odoo CE 19 trên Ubuntu bằng Docker Compose
Hướng dẫn từng bước cài Odoo CE 19 production-ready trên Ubuntu 22.04/24.04 với docker-compose, PostgreSQL 16, nginx reverse proxy và SSL Let's Encrypt.

Cài đặt Odoo CE 19 trên Ubuntu bằng Docker Compose
Odoo CE 19 ra mắt với nhiều cải tiến về hiệu năng ORM và web client. Bài viết này hướng dẫn dựng một stack production-ready trên một VPS Ubuntu duy nhất, dùng docker-compose để quản lý lifecycle, PostgreSQL 16 cho database, và nginx làm reverse proxy với SSL từ Let's Encrypt. Toàn bộ stack chạy được trên VPS 2 vCPU / 4GB RAM phục vụ 20-30 user concurrent — đủ cho phần lớn SME Việt Nam ở giai đoạn pilot.
Vì sao chọn docker-compose thay vì cài native bằng apt?
Cài Odoo native qua package .deb từ nightly.odoo.com vẫn chạy tốt, nhưng có ba điểm yếu khi vận hành dài hạn:
- Upgrade khó rollback: native install ghi đè binary trực tiếp. Khi
apt upgradehỏng (ví dụ Python lib version mismatch), khôi phục mất 1-2 giờ. - Phụ thuộc Python system: Odoo 19 yêu cầu Python 3.11+. Nếu Ubuntu 22.04 default 3.10, phải dùng PPA
deadsnakes— gây xung đột với các app khác cùng máy. - Multi-instance phức tạp: chạy 2 instance Odoo (test + prod) trên cùng máy với native cần manually quản lý systemd unit + port + workers config.
Docker giải quyết cả ba: image official odoo:19 từ Docker Hub đã đóng gói đúng Python runtime, rollback chỉ cần đổi tag image, và mỗi instance là một compose stack độc lập. Trade-off: docker thêm ~150MB overhead RAM, và debug đôi lúc cần docker exec thay vì tail trực tiếp file log.
Chuẩn bị server
Mặc định bạn đã có VPS Ubuntu 22.04 hoặc 24.04 với user non-root có quyền sudo. Cài Docker engine theo hướng dẫn chính thức:
# Cài docker + compose plugin
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER
# Logout + login lại để group có hiệu lực
Verify:
docker --version # 24.x trở lên
docker compose version # v2.20+
Cấu trúc thư mục
Tạo project directory tách biệt cho từng instance:
mkdir -p ~/odoo-prod/{config,addons,postgres-data,odoo-data}
cd ~/odoo-prod
Layout sau khi xong:
odoo-prod/
├── docker-compose.yml
├── .env
├── config/
│ └── odoo.conf
├── addons/ # custom modules mount vào container
├── postgres-data/ # persistent volume cho postgres
└── odoo-data/ # filestore + sessions
File .env — quản lý secrets
Tách credential ra file riêng, không commit vào git:
# .env
POSTGRES_USER=odoo
POSTGRES_PASSWORD=ChangeMe_StrongPassword_2026
POSTGRES_DB=postgres
ODOO_ADMIN_PASSWD=AdminMasterPassword_NeverShare
DOMAIN=odoo.your-site.example.com
ODOO_ADMIN_PASSWD là master password để tạo/restore database từ web UI /web/database/manager. Đặt mật khẩu mạnh và không bao giờ chia sẻ — ai có nó đều có thể drop database của bạn.
File config/odoo.conf
[options]
addons_path = /mnt/extra-addons
data_dir = /var/lib/odoo
admin_passwd = $ODOO_ADMIN_PASSWD
db_host = db
db_port = 5432
db_user = odoo
db_password = $POSTGRES_PASSWORD
db_maxconn = 64
proxy_mode = True
workers = 3
max_cron_threads = 1
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_request = 8192
limit_time_cpu = 600
limit_time_real = 1200
log_level = info
Hai option quan trọng:
proxy_mode = True— báo Odoo rằng nó đứng sau reverse proxy (nginx). Không bật flag này thì tất cả URL Odoo generate sẽ làhttp://thay vìhttps://, dẫn đến mixed-content errors trên browser.workers = 3— multi-process mode. Công thức quen dùng:workers = (CPU * 2) + 1. Trên VPS 2 vCPU đặt 3-5 là vừa. Đặtworkers = 0chạy thread mode, chỉ phù hợp dev.
File docker-compose.yml
services:
db:
image: postgres:16-alpine
container_name: odoo-db
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- ./postgres-data:/var/lib/postgresql/data
networks:
- odoo-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
odoo:
image: odoo:19
container_name: odoo-app
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports:
- "127.0.0.1:8069:8069"
- "127.0.0.1:8072:8072"
environment:
HOST: db
USER: ${POSTGRES_USER}
PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- ./config:/etc/odoo
- ./addons:/mnt/extra-addons
- ./odoo-data:/var/lib/odoo
networks:
- odoo-net
networks:
odoo-net:
driver: bridge
Lưu ý port 8069 (HTTP) và 8072 (longpolling/chat) chỉ bind vào 127.0.0.1. Public traffic đi qua nginx — không bao giờ expose Odoo trực tiếp ra internet.
Khởi động stack:
docker compose up -d
docker compose logs -f odoo # theo dõi đến khi thấy "HTTP service running"
Truy cập tạm http://your-vps-ip:8069 để check (sẽ chặn sau khi gắn nginx).
Reverse proxy nginx + SSL Let's Encrypt
Cài nginx + certbot:
sudo apt-get install -y nginx certbot python3-certbot-nginx
Tạo /etc/nginx/sites-available/odoo:
upstream odoo_http {
server 127.0.0.1:8069;
}
upstream odoo_longpolling {
server 127.0.0.1:8072;
}
server {
listen 80;
server_name odoo.your-site.example.com;
# Để certbot lấy cert, sau đó certbot tự thêm 301 redirect
location /.well-known/acme-challenge/ {
root /var/www/html;
}
}
server {
listen 443 ssl http2;
server_name odoo.your-site.example.com;
# SSL cert sẽ được certbot inject
proxy_read_timeout 720s;
proxy_connect_timeout 720s;
proxy_send_timeout 720s;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 100M;
location /websocket {
proxy_pass http://odoo_longpolling;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / {
proxy_pass http://odoo_http;
proxy_redirect off;
}
gzip on;
gzip_min_length 1100;
gzip_buffers 4 32k;
gzip_types text/css text/plain text/xml application/xml application/javascript application/json;
gzip_vary on;
}
Enable site và xin cert:
sudo ln -s /etc/nginx/sites-available/odoo /etc/nginx/sites-enabled/
sudo nginx -t # test config
sudo systemctl reload nginx
sudo certbot --nginx -d odoo.your-site.example.com
Certbot sẽ tự edit nginx config thêm ssl_certificate + 301 redirect HTTP → HTTPS, đồng thời cài cron job auto-renew chạy hai lần một ngày.
Hardening cuối cùng
Khi mọi thứ chạy ổn, vô hiệu hóa database manager để tránh lộ master password endpoint:
# Thêm vào config/odoo.conf
list_db = False
Và thiết lập firewall:
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Restart Odoo: docker compose restart odoo. Truy cập https://odoo.your-site.example.com → tạo database đầu tiên qua wizard, sau đó tắt list_db.
Backup chiến lược tối thiểu
Tạo script ~/odoo-prod/backup.sh:
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR=/var/backups/odoo
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
mkdir -p "$BACKUP_DIR"
# Dump database
docker compose exec -T db pg_dump -U odoo -F c postgres \
> "$BACKUP_DIR/db-$TIMESTAMP.dump"
# Snapshot filestore
tar czf "$BACKUP_DIR/filestore-$TIMESTAMP.tar.gz" -C ./odoo-data .
# Giữ 7 ngày gần nhất
find "$BACKUP_DIR" -name "*.dump" -mtime +7 -delete
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
Cài cron 0 2 * * * cd ~/odoo-prod && ./backup.sh để chạy 2h sáng mỗi ngày. Quan trọng nhất: định kỳ test restore vào staging — backup chưa từng restore không phải là backup.
Khi nào nên rời docker-compose?
Stack này phục vụ tốt đến khoảng 50 concurrent user và 1 instance Odoo. Khi nhu cầu vượt qua đó, cân nhắc:
- Tách PostgreSQL ra managed service (DigitalOcean Managed DB, AWS RDS) — giảm rủi ro mất data do disk failure trên VPS
- Chuyển sang Kubernetes nếu chạy ≥5 instance với CI/CD multi-environment
- Dùng load balancer đứng trước 2-3 Odoo container để failover
Với 90% SME ở Việt Nam, single-VPS docker-compose đủ dùng 2-3 năm trước khi cần scale ngang.
References: