Self-Hosting Odoo on Ubuntu 24.04: The Complete 2026 Guide
A production-ready Odoo server installation on Ubuntu 24.04 LTS, from a blank VPS to a hardened, SSL-terminated, monitored Odoo 18 instance. This guide covers Nginx reverse proxy, PostgreSQL tuning, Fail2ban, and automated Let's Encrypt renewal.
Prerequisites
Before starting, you need:
- A VPS or dedicated server with Ubuntu 24.04 LTS
- Minimum 4GB RAM (8GB recommended for production)
- A registered domain pointed to your server's IP
- Root or sudo access
This guide targets Odoo 18 Community Edition. The same steps apply to Odoo 17 with minor version number changes.
1. System Preparation
# Update system packages
sudo apt update && sudo apt upgrade -y
# Install dependencies
sudo apt install -y python3-pip python3-dev python3-venv python3-wheel libxml2-dev libpq-dev libjpeg8-dev liblcms2-dev libxslt1-dev zlib1g-dev libsasl2-dev libldap2-dev build-essential git libssl-dev libffi-dev libmysqlclient-dev libjpeg-dev libblas-dev libatlas-base-dev npm nodejs wkhtmltopdf
# Install wkhtmltopdf (required for PDF generation)
# Ubuntu 24.04 includes a version that works with Odoo 18
sudo ln -sf /usr/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
2. PostgreSQL Installation and Configuration
sudo apt install -y postgresql postgresql-contrib
# Create Odoo PostgreSQL user
sudo -u postgres createuser --createdb --no-createrole --no-superuser odoo18
# Set password
sudo -u postgres psql -c "ALTER USER odoo18 PASSWORD 'your_strong_db_password';"
PostgreSQL Performance Tuning
For a 4GB RAM server, add these settings to /etc/postgresql/16/main/postgresql.conf:
# Memory settings
shared_buffers = 1GB # 25% of RAM
effective_cache_size = 3GB # 75% of RAM
work_mem = 16MB # Per-sort / per-hash operation
maintenance_work_mem = 256MB # For VACUUM, CREATE INDEX
# Query planner
default_statistics_target = 100
random_page_cost = 1.1 # For SSD storage; use 4.0 for HDD
# Checkpoints
checkpoint_completion_target = 0.9
wal_buffers = 16MB
# Connection settings
max_connections = 100
sudo systemctl restart postgresql
3. Create Odoo System User
sudo adduser --system --home=/opt/odoo18 --group odoo18
4. Install Odoo 18
# Clone Odoo Community Edition
sudo git clone --depth=1 --branch=18.0 https://github.com/odoo/odoo.git /opt/odoo18/odoo
# Create virtual environment
sudo -u odoo18 python3 -m venv /opt/odoo18/venv
# Install Python dependencies
sudo -u odoo18 /opt/odoo18/venv/bin/pip install --upgrade pip setuptools wheel
sudo -u odoo18 /opt/odoo18/venv/bin/pip install -r /opt/odoo18/odoo/requirements.txt
# Create addons and config directories
sudo -u odoo18 mkdir /opt/odoo18/addons
sudo -u odoo18 mkdir /opt/odoo18/config
sudo -u odoo18 mkdir /opt/odoo18/data
5. Odoo Configuration File
Create /opt/odoo18/config/odoo18.conf:
[options]
; Administration
admin_passwd = your_master_password_here
db_host = 127.0.0.1
db_port = 5432
db_user = odoo18
db_password = your_strong_db_password
; Paths
addons_path = /opt/odoo18/odoo/addons,/opt/odoo18/addons
data_dir = /opt/odoo18/data
; Performance
workers = 4 ; Number of worker processes (CPU count × 2)
max_cron_threads = 1
limit_memory_hard = 2684354560 ; 2.5GB hard limit per worker
limit_memory_soft = 2147483648 ; 2GB soft limit per worker
limit_time_cpu = 600
limit_time_real = 1200
limit_request = 8192
; Logging
logfile = /var/log/odoo18/odoo18.log
log_level = warn
; Network (Nginx handles HTTPS upstream)
proxy_mode = True
xmlrpc_port = 8069
longpolling_port = 8072
6. Systemd Service
Create /etc/systemd/system/odoo18.service:
[Unit]
Description=Odoo 18 Community
Requires=postgresql.service
After=network.target postgresql.service
[Service]
Type=simple
SyslogIdentifier=odoo18
PermissionsStartOnly=true
User=odoo18
Group=odoo18
ExecStart=/opt/odoo18/venv/bin/python3 /opt/odoo18/odoo/odoo-bin --config=/opt/odoo18/config/odoo18.conf
StandardOutput=journal+console
[Install]
WantedBy=multi-user.target
sudo mkdir /var/log/odoo18
sudo chown odoo18:odoo18 /var/log/odoo18
sudo systemctl daemon-reload
sudo systemctl enable odoo18
sudo systemctl start odoo18
sudo systemctl status odoo18
7. Nginx Reverse Proxy with SSL
sudo apt install -y nginx certbot python3-certbot-nginx
Create /etc/nginx/sites-available/odoo18.conf:
# Redirect HTTP to HTTPS
server {
listen 80;
server_name odoo.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name odoo.yourdomain.com;
# SSL (managed by certbot after initial setup)
ssl_certificate /etc/letsencrypt/live/odoo.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/odoo.yourdomain.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
# Proxy to Odoo
location / {
proxy_pass http://127.0.0.1:8069;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 720s;
proxy_connect_timeout 720s;
proxy_send_timeout 720s;
client_max_body_size 100m;
}
# Longpolling for real-time features (chat, IoT)
location /longpolling {
proxy_pass http://127.0.0.1:8072;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Cache static assets
location ~* .(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
proxy_pass http://127.0.0.1:8069;
proxy_set_header Host $host;
expires 7d;
add_header Cache-Control "public, immutable";
}
}
sudo ln -s /etc/nginx/sites-available/odoo18.conf /etc/nginx/sites-enabled/
sudo nginx -t
# Issue SSL certificate
sudo certbot --nginx -d odoo.yourdomain.com
sudo systemctl reload nginx
8. Fail2ban for Brute Force Protection
sudo apt install -y fail2ban
Create /etc/fail2ban/jail.d/odoo18.conf:
[odoo18]
enabled = true
port = http,https
backend = auto
filter = odoo18
logpath = /var/log/odoo18/odoo18.log
maxretry = 10
findtime = 600
bantime = 3600
Create /etc/fail2ban/filter.d/odoo18.conf:
[Definition]
failregex = ^.*Login attempt from <HOST> failed for.*$
^.*Login failed for.*from <HOST>.*$
ignoreregex =
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status odoo18
9. Automated Backups
Create /opt/odoo18/scripts/backup.sh:
#!/bin/bash
BACKUP_DIR="/opt/odoo18/backups"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="your_odoo_database"
mkdir -p "$BACKUP_DIR"
# Database backup
pg_dump -U odoo18 -h 127.0.0.1 "$DB_NAME" | gzip > "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
# Data directory backup (filestore, sessions)
tar -czf "$BACKUP_DIR/filestore_${DATE}.tar.gz" /opt/odoo18/data/filestore/
# Remove backups older than 30 days
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -delete
echo "Backup completed: ${DATE}"
chmod +x /opt/odoo18/scripts/backup.sh
# Schedule daily backup at 2 AM
echo "0 2 * * * odoo18 /opt/odoo18/scripts/backup.sh >> /var/log/odoo18/backup.log 2>&1" | sudo tee -a /etc/crontab
10. Monitoring with Odoo's Built-in Tools
Once running, check performance via:
- Odoo Debug Mode: Enable via Settings → Activate Developer Mode
- Query count: Add
?debug=1to any Odoo URL + check the dev toolbar query count
For external monitoring, consider:
- Prometheus + node_exporter: Server-level metrics (CPU, RAM, disk I/O)
- pgBadger: PostgreSQL query analysis from log files
- NonaGuard: Automated Odoo security scanning, CVE detection, and upgrade readiness assessment — designed specifically for Odoo instances
Common Issues
wkhtmltopdf returns blank PDFs: Ubuntu 24.04's wkhtmltopdf may require Xvfb for headless rendering on servers without a display. Install:
sudo apt install xvfb
# Then configure wkhtmltopdf path in Odoo settings to use xvfb-run wrapper
Worker OOM kills: Increase limit_memory_hard in odoo.conf, or reduce workers count. Monitor with journalctl -u odoo18 -f.
Slow module list loading: Run UPDATE ir_module_module SET state = 'uninstalled' WHERE state = 'to remove'; in psql if you have residual module states after failed installs.
Need help with a complex Odoo infrastructure setup? Hexalian's infrastructure hardening service covers production server setup, security hardening, and disaster recovery for Odoo deployments.
Browse Odoo modules at hexalian.com/store or run a free Odoo health scan at nonaguard.com (7-day trial).
Running Odoo in production?
Get Odoo modules with full source code, or scan your instance for security and performance issues in under a minute.