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.
Looking for a specific Odoo ecommerce module or comparing your options for an Odoo modules list in 2026? Visit the store to review module features, pricing, and compatibility before implementation.
Need Odoo engineering for your business?