Skip to content
·15 min read·By Hexalian Engineering

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:

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:

For external monitoring, consider:

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?

Browse ModulesContact Us

Recommended Odoo Modules

NonaGuard — Odoo Security Audit & Health Monitor
NonaGuard — Odoo Security Audit & Health Monitor
Free · Technical
View
Website Promo Banner & Announcement Bar | Sticky Header, Countdown Timer, Analytics
Website Promo Banner & Announcement Bar | Sticky Header, Countdown Timer, Analytics
From $56.99 · Website/eCommerce
View
B2B Quick Order & Bulk Order Form | CSV Upload, Clipboard Paste, Saved Lists
B2B Quick Order & Bulk Order Form | CSV Upload, Clipboard Paste, Saved Lists
From $91.99 · Website/eCommerce
View