Skip to main content
Step-by-Step Guide

Install Odoo on Ubuntu 22.04 & 24.04

Complete Odoo 18 installation on a fresh Ubuntu server. Python, PostgreSQL, wkhtmltopdf, Nginx, SSL — everything configured and hardened for production. Tested on both Ubuntu 22.04 LTS and 24.04 LTS.

15 min read
Updated February 2026
Odoo 18 / Ubuntu 22.04 & 24.04

This guide walks through a complete Odoo 18 installation on a fresh Ubuntu server. Every step has been tested on both Ubuntu 22.04 LTS (Jammy) and 24.04 LTS (Noble). By the end, you will have Odoo running behind Nginx with SSL, managed by systemd, and hardened for production.

If you want a Docker-based installation instead, see our Docker Compose guide. If you would rather skip the manual setup entirely, OEC.sh deploys Odoo on any cloud provider in under 5 minutes.

Prerequisites

Before starting, make sure you have:

  • Ubuntu 22.04 LTS or 24.04 LTS — a fresh server installation (minimal or standard). Other Ubuntu versions may work but are not covered here.
  • Root or sudo access — you need to install packages and create system users.
  • A domain name pointed at your server's IP — for example, odoo.yourcompany.com. Set an A record in your DNS provider now, because DNS propagation can take up to an hour.
  • At least 2 GB of RAM — Odoo can run on 1 GB for testing, but production needs 2 GB minimum. For worker-based setups with concurrent users, 4 GB or more is recommended. Use our Server Requirements Calculator to size your instance properly.
  • 20 GB of disk space — for Odoo source, PostgreSQL data, and the filestore. Plan for more if you expect heavy attachment uploads.

Verify your Ubuntu version:

Terminalbash
lsb_release -a

You should see 22.04 or 24.04 in the output.

Install System Dependencies

Odoo 18 requires Python 3.11 or later, PostgreSQL 16, wkhtmltopdf for PDF report rendering, and Node.js for compiling LESS/SCSS assets.

Update the system

Terminalbash
sudo apt update && sudo apt upgrade -y

Install Python and build tools

Ubuntu 24.04 ships with Python 3.12. Ubuntu 22.04 ships with Python 3.10, which is too old for Odoo 18. If you are on 22.04, add the deadsnakes PPA:

Ubuntu 22.04 onlybash
# Ubuntu 22.04 only — add deadsnakes PPA for Python 3.12
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:deadsnakes/ppa
sudo apt update
sudo apt install -y python3.12 python3.12-venv python3.12-dev
Ubuntu 24.04bash
# Ubuntu 24.04 — Python 3.12 is already available
sudo apt install -y python3 python3-venv python3-dev

Install the build dependencies Odoo needs for compiling Python packages:

Terminalbash
sudo apt install -y build-essential libxml2-dev libxslt1-dev \
  libevent-dev libpq-dev libldap2-dev libsasl2-dev \
  libssl-dev libjpeg-dev libfreetype6-dev zlib1g-dev \
  libffi-dev git wget curl

Install PostgreSQL 16

Terminalbash
# Add the official PostgreSQL repository
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt update
sudo apt install -y postgresql-16

Verify PostgreSQL is running:

Terminalbash
sudo systemctl status postgresql

Install wkhtmltopdf

Odoo uses wkhtmltopdf to render HTML reports (invoices, purchase orders, etc.) as PDFs. The version in Ubuntu's default repositories is outdated and missing the patched Qt WebKit that Odoo requires. Install the correct version directly:

Terminalbash
# Download the patched wkhtmltopdf (0.12.6.1 for amd64)
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.jammy_amd64.deb

# Install it (will pull in dependencies automatically)
sudo apt install -y ./wkhtmltox_0.12.6.1-3.jammy_amd64.deb

Note for Ubuntu 24.04: The Jammy .deb file installs cleanly on Noble as well. If you encounter dependency issues, install the missing libraries with sudo apt install -y libssl3 libpng16-16.

Verify the installation:

Terminalbash
wkhtmltopdf --version
# Expected: wkhtmltopdf 0.12.6 (with patched qt)

Install Node.js

Odoo needs Node.js for compiling SCSS/LESS frontend assets:

Terminalbash
sudo apt install -y nodejs npm
sudo npm install -g rtlcss

If you need a more recent Node.js version, use the NodeSource repository, but the default Ubuntu packages work fine for Odoo.

Create Odoo System User and Directory Structure

Never run Odoo as root. Create a dedicated system user:

Terminalbash
sudo useradd -m -d /opt/odoo -U -r -s /bin/bash odoo

This creates:

  • A system user named odoo (no login shell for interactive sessions)
  • A home directory at /opt/odoo
  • A group also named odoo

Create the directory structure:

Terminalbash
sudo mkdir -p /opt/odoo/{custom-addons,config,logs}
sudo chown -R odoo:odoo /opt/odoo

The layout will look like:

Directory Structuretext
/opt/odoo/
├── odoo/              # Odoo source code (from git)
├── venv/              # Python virtual environment
├── custom-addons/     # Your custom and third-party modules
├── config/            # odoo.conf
└── logs/              # Application logs

Install Odoo from Source

Installing from source gives you full control over the version, lets you apply patches, and makes it easy to pull updates. This is the recommended method for production.

Clone the Odoo source

Terminalbash
sudo su - odoo
git clone https://www.github.com/odoo/odoo --depth 1 --branch 18.0 /opt/odoo/odoo

The --depth 1 flag creates a shallow clone (only the latest commit), saving disk space and download time. Replace 18.0 with 17.0 or 19.0 if you need a different version.

Create a virtual environment and install Python dependencies

Terminal (as odoo user)bash
# Still as the odoo user
python3.12 -m venv /opt/odoo/venv
source /opt/odoo/venv/bin/activate
pip install --upgrade pip wheel
pip install -r /opt/odoo/odoo/requirements.txt

This will take a few minutes. The requirements.txt pulls in around 80 packages including lxml, Pillow, psycopg2, Werkzeug, and the rest of Odoo's dependency tree.

Exit the odoo user session when done:

Terminalbash
deactivate
exit

Install Odoo from APT Package

If you prefer a simpler installation managed by APT (automatic updates, standard Debian packaging), Odoo publishes an official repository.

Trade-off: The APT package is easier to install but harder to customize. You cannot easily patch the source, run multiple versions side-by-side, or use a virtual environment. For production, the source installation above is generally preferred.

Terminalbash
# Add Odoo's official GPG key and repository
wget -qO - https://nightly.odoo.com/odoo.key | sudo gpg --dearmor -o /usr/share/keyrings/odoo-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/odoo-archive-keyring.gpg] https://nightly.odoo.com/18.0/nightly/deb/ ./" | sudo tee /etc/apt/sources.list.d/odoo.list

sudo apt update
sudo apt install -y odoo

This installs Odoo 18 as a system package, creates the odoo user, installs a default configuration at /etc/odoo/odoo.conf, and sets up a systemd service automatically.

To check the status:

Terminalbash
sudo systemctl status odoo

If you used the APT method, skip the systemd and directory structure sections below — the package handles those. You should still configure PostgreSQL, Nginx, and production hardening.

Configure PostgreSQL

Odoo needs a PostgreSQL role that can create databases. Create one that matches the odoo system user:

Terminalbash
sudo su - postgres
createuser --createdb --no-superuser --no-createrole --pwprompt odoo

Enter a strong password when prompted. Save this password — you will need it for odoo.conf.

Harden PostgreSQL authentication

Edit the PostgreSQL client authentication file:

Terminalbash
sudo nano /etc/postgresql/16/main/pg_hba.conf

Find the line for local connections and ensure it uses scram-sha-256 (not peer or trust):

pg_hba.conftext
# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             odoo                                    scram-sha-256
host    all             odoo            127.0.0.1/32            scram-sha-256
host    all             odoo            ::1/128                 scram-sha-256

If you changed anything, restart PostgreSQL:

Terminalbash
sudo systemctl restart postgresql

Verify the connection works:

Terminalbash
psql -U odoo -d postgres -h 127.0.0.1 -c "SELECT version();"

Configure Odoo

Create the main Odoo configuration file:

Terminalbash
sudo nano /opt/odoo/config/odoo.conf

Paste the following configuration and adjust the values:

odoo.confini
[options]
; Database
db_host = 127.0.0.1
db_port = 5432
db_user = odoo
db_password = YOUR_STRONG_PASSWORD_HERE
db_name = False
db_maxconn = 64
list_db = False

; Paths
addons_path = /opt/odoo/odoo/addons,/opt/odoo/custom-addons
data_dir = /opt/odoo/.local/share/Odoo

; Security
admin_passwd = CHANGE_THIS_MASTER_PASSWORD
proxy_mode = True

; Workers (production — adjust based on CPU cores)
; Formula: workers = (CPU cores * 2) + 1
workers = 5
max_cron_threads = 2
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_time_cpu = 600
limit_time_real = 1200
limit_time_real_cron = -1
limit_request = 8192

; Logging
logfile = /opt/odoo/logs/odoo.log
log_level = warn
log_handler = :WARNING
logrotate = True

; Longpolling (required when workers > 0)
gevent_port = 8072

; Network
http_port = 8069
xmlrpc_interface = 127.0.0.1
netrpc_interface = 127.0.0.1

Set the file permissions so only the odoo user can read it (it contains passwords):

Terminalbash
sudo chown odoo:odoo /opt/odoo/config/odoo.conf
sudo chmod 640 /opt/odoo/config/odoo.conf

Key settings explained

  • proxy_mode = True — required when Odoo sits behind Nginx. Tells Odoo to trust X-Forwarded-* headers for correct IP logging and HTTPS detection.
  • workers = 5 — enables multi-process mode. Without this, Odoo runs single-threaded and cannot handle concurrent requests. The formula (CPU * 2) + 1 is a reasonable starting point for a 2-core machine.
  • list_db = False — hides the database manager from the web UI. Important for security in production.
  • admin_passwd — the master password for creating/deleting databases via the web UI. Set a strong value or disable database management entirely.

Configure Nginx Reverse Proxy

Nginx handles SSL termination, serves static files efficiently, and proxies requests to Odoo. This is essential for production.

Install Nginx and Certbot

Terminalbash
sudo apt install -y nginx certbot python3-certbot-nginx

Create the Nginx server block

Terminalbash
sudo nano /etc/nginx/sites-available/odoo
/etc/nginx/sites-available/odoonginx
upstream odoo {
    server 127.0.0.1:8069;
}

upstream odoo-chat {
    server 127.0.0.1:8072;
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name odoo.yourcompany.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name odoo.yourcompany.com;

    # SSL certificates (managed by Certbot)
    ssl_certificate /etc/letsencrypt/live/odoo.yourcompany.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/odoo.yourcompany.com/privkey.pem;

    # SSL hardening
    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;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;

    # Security headers
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;

    # Logging
    access_log /var/log/nginx/odoo-access.log;
    error_log /var/log/nginx/odoo-error.log;

    # Proxy buffers for large Odoo responses
    proxy_buffers 16 64k;
    proxy_buffer_size 128k;
    proxy_read_timeout 900s;
    proxy_connect_timeout 900s;
    proxy_send_timeout 900s;

    # Request size (for file uploads)
    client_max_body_size 200m;

    # Gzip compression
    gzip on;
    gzip_types text/css text/plain text/xml application/xml application/json application/javascript;
    gzip_min_length 1000;

    # Longpolling (live chat, notifications)
    location /websocket {
        proxy_pass http://odoo-chat;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;
    }

    # All other Odoo traffic
    location / {
        proxy_pass http://odoo;
        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_redirect off;
    }

    # Static file caching
    location ~* /web/static/ {
        proxy_pass http://odoo;
        proxy_cache_valid 200 90m;
        proxy_buffering on;
        expires 7d;
        add_header Cache-Control "public, no-transform";
    }
}

Enable the site and obtain SSL

Terminalbash
# Enable the site
sudo ln -s /etc/nginx/sites-available/odoo /etc/nginx/sites-enabled/odoo
sudo rm /etc/nginx/sites-enabled/default

# Test configuration (ignore SSL errors — we have not generated certs yet)
sudo nginx -t

# Get SSL certificate from Let's Encrypt
# First, temporarily comment out the SSL server block and the HTTP redirect,
# add a plain HTTP server block, then run:
sudo certbot --nginx -d odoo.yourcompany.com --non-interactive --agree-tos --email your@email.com

# Certbot will modify the Nginx config to add SSL certificate paths
# and set up automatic renewal

# Verify auto-renewal is working
sudo certbot renew --dry-run

Restart Nginx:

Terminalbash
sudo systemctl restart nginx

Set Up Systemd Service

Create a systemd unit file so Odoo starts automatically on boot and restarts on failure:

Terminalbash
sudo nano /etc/systemd/system/odoo.service
odoo.serviceini
[Unit]
Description=Odoo 18
Documentation=https://www.odoo.com/documentation/18.0/
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=simple
User=odoo
Group=odoo
ExecStart=/opt/odoo/venv/bin/python3 /opt/odoo/odoo/odoo-bin \
    --config=/opt/odoo/config/odoo.conf

# Restart policy
Restart=on-failure
RestartSec=5s

# Resource limits
LimitNOFILE=65536
LimitNPROC=4096

# Security hardening
PrivateTmp=true
ProtectSystem=full
NoNewPrivileges=true

# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=odoo

[Install]
WantedBy=multi-user.target

Enable and start the service:

Terminalbash
sudo systemctl daemon-reload
sudo systemctl enable odoo
sudo systemctl start odoo

Check that Odoo is running:

Terminalbash
sudo systemctl status odoo
sudo journalctl -u odoo -f --no-pager -n 50

You should see Odoo loading modules and listening on port 8069.

Post-Install Verification

With Odoo running behind Nginx with SSL, open your browser and navigate to:

Browsertext
https://odoo.yourcompany.com

You should see the Odoo database creation page (or the login page if list_db = False and you already created a database).

Create your first database

If list_db is set to True (or you access the database manager directly at /web/database/manager):

  1. Enter the master password (the admin_passwd from odoo.conf).
  2. Choose a database name (e.g., production).
  3. Set the admin email and password.
  4. Select your language and country.
  5. Check “Demo data” only if this is a test instance.
  6. Click Create Database.

Database creation takes 30–60 seconds. Odoo will install the base modules and redirect you to the home screen.

Verify core functionality

  • Navigate to Settings > General Settings to confirm the admin panel loads.
  • Install the CRM or Sales module to verify module installation works.
  • Go to Settings > Technical > System Parameters and confirm web.base.url matches your domain.
  • Print any report to PDF (e.g., a sample invoice) to verify wkhtmltopdf is working.

Production Hardening

A running Odoo instance is only half the job. Production systems need firewall rules, intrusion detection, and log management.

Configure UFW firewall

Terminalbash
# Allow SSH, HTTP, HTTPS only
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Verify:

Terminalbash
sudo ufw status verbose

The Odoo ports (8069, 8072) should NOT be exposed publicly — Nginx proxies all traffic.

Install fail2ban

Protect against brute-force SSH and Odoo login attempts:

Terminalbash
sudo apt install -y fail2ban

Create a custom Odoo jail:

Terminalbash
sudo nano /etc/fail2ban/jail.d/odoo.conf
/etc/fail2ban/jail.d/odoo.confini
[odoo-login]
enabled = true
port = http,https
filter = odoo-login
logpath = /opt/odoo/logs/odoo.log
maxretry = 5
bantime = 900
findtime = 600

Create the filter:

Terminalbash
sudo nano /etc/fail2ban/filter.d/odoo-login.conf
/etc/fail2ban/filter.d/odoo-login.confini
[Definition]
failregex = ^ \d+ WARNING .* Login failed for db:.* login:.*  from <HOST>
ignoreregex =
Terminalbash
sudo systemctl restart fail2ban

Set up log rotation

If Odoo's built-in logrotate = True setting is not sufficient, add a system-level logrotate config:

Terminalbash
sudo nano /etc/logrotate.d/odoo
/etc/logrotate.d/odootext
/opt/odoo/logs/odoo.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
    copytruncate
}

Enable automatic security updates

Terminalbash
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

This ensures your Ubuntu server receives critical security patches without manual intervention.

Common Installation Issues and Fixes

wkhtmltopdf renders blank or broken PDFs

Symptom: PDF reports are empty, garbled, or throw an error about missing fonts.

Fix: The system wkhtmltopdf (from apt install wkhtmltopdf) lacks the patched Qt WebKit. You must install the specific .deb from the wkhtmltopdf GitHub releases as shown in the dependencies section. Also install common fonts:

Terminalbash
sudo apt install -y fonts-liberation fonts-dejavu-core fontconfig
sudo fc-cache -f -v

Locale errors during installation

Symptom: Errors like locale.Error: unsupported locale setting when Odoo starts.

Fix:

Terminalbash
sudo locale-gen en_US.UTF-8
sudo update-locale LANG=en_US.UTF-8
# Log out and back in, or run:
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

Permission denied errors

Symptom: Odoo cannot write to the filestore or log directory.

Fix: Ensure the odoo user owns all relevant directories:

Terminalbash
sudo chown -R odoo:odoo /opt/odoo
sudo chown -R odoo:odoo /opt/odoo/.local

PostgreSQL connection refused

Symptom: psycopg2.OperationalError: could not connect to server: Connection refused

Fix: Check three things:

  1. PostgreSQL is running: sudo systemctl status postgresql
  2. The db_host in odoo.conf matches your pg_hba.conf entries. If you use 127.0.0.1, the pg_hba.conf must have a host line for 127.0.0.1/32. If you use localhost, it might try a Unix socket — use the explicit IP.
  3. The odoo PostgreSQL role exists: sudo -u postgres psql -c "\du" should list the odoo role.

“413 Request Entity Too Large” on file uploads

Symptom: Uploading attachments larger than 1 MB fails with an Nginx error.

Fix: Increase client_max_body_size in the Nginx server block:

Nginx confignginx
client_max_body_size 200m;

Then reload Nginx: sudo systemctl reload nginx

Odoo is extremely slow with a single worker

Symptom: Pages take 10+ seconds to load, especially with multiple users.

Fix: You are running in single-process mode. Set workers in odoo.conf to at least (CPU cores * 2) + 1. With workers enabled, Odoo spawns separate processes for handling requests and a gevent process for longpolling on port 8072.

Or Deploy in 5 Minutes with OEC.sh

Everything above — Python, PostgreSQL, Nginx, SSL, systemd, firewall, log rotation — is roughly 45 minutes of work if everything goes perfectly, and a few hours if you hit compatibility issues.

OEC.sh handles all of it:

  • Pick your cloud provider — DigitalOcean, AWS, Google Cloud, Azure, Hetzner, Vultr, OVH, or any other provider. No vendor lock-in.
  • Choose your Odoo version — 16, 17, 18, or 19. Community or Enterprise.
  • Deploy — SSL, backups, monitoring, and updates are configured automatically.
  • Free tier available — start without a credit card.

The manual install above is the right choice if you want complete control over every configuration detail. OEC.sh is the right choice if you want to get Odoo running today and focus on your business instead of your infrastructure.

Skip the manual setup

OEC.sh deploys Odoo on any cloud provider with SSL, backups, and monitoring — in under 5 minutes. No server configuration needed.

  • Free tier available
  • No credit card required
  • Any cloud provider
Deploy Odoo Free

Frequently Asked Questions

What Ubuntu version should I use for Odoo 18?

Ubuntu 24.04 LTS (Noble) is the best choice for new installations. It ships with Python 3.12, which Odoo 18 supports natively without adding third-party PPAs. Ubuntu 22.04 LTS (Jammy) also works but requires adding the deadsnakes PPA for Python 3.11 or 3.12. Both are supported LTS releases with security updates through 2027 (22.04) and 2029 (24.04).

How much RAM does Odoo need on Ubuntu?

Minimum 2 GB for a basic installation with a handful of users. For production with 10–50 concurrent users, plan for 4–8 GB. With workers enabled (which you should always do in production), each Odoo worker process uses 150–300 MB of RAM. The formula is roughly: (number of workers × 250 MB) + 1 GB for PostgreSQL + 512 MB for the OS.

Can I install multiple Odoo versions on the same server?

Yes, if you use the source installation method with separate virtual environments. Create distinct system users (e.g., odoo17, odoo18), clone each version into its own directory, and run them on different ports. Each version needs its own odoo.conf, systemd service, and Nginx upstream block. That said, this gets complex fast — consider Docker containers for multi-version setups.

Should I install Odoo from source or from the APT package?

Install from source if you need fine-grained control: custom patches, specific Python package versions, virtual environments, or side-by-side installations. Install from APT if you want a simpler setup with automatic updates through the package manager. Most production deployments use the source method because it provides more flexibility for upgrades and rollbacks.

How do I update Odoo after installation?

For a source installation: stop the Odoo service, pull the latest changes from the Odoo Git repository (git pull), update pip packages (pip install -r requirements.txt), restart the service, and run the database upgrade if needed (odoo-bin -u all -d your_database --stop-after-init). Always back up your database before upgrading. For APT installations: sudo apt update && sudo apt upgrade odoo.

Is it better to use Docker or a native Ubuntu installation?

Neither is universally better — it depends on your workflow. Native installation gives you direct access to all system components, easier debugging, and no container abstraction layer. Docker gives you reproducible environments, easier rollbacks (swap the image tag), and cleaner multi-service setups. If your team is already comfortable with Docker, use Docker. If you prefer managing services directly on the OS, use the native installation covered in this guide.

Last updated: February 2026. Tested with Odoo 18.0, Ubuntu 22.04.5 LTS, and Ubuntu 24.04.1 LTS.

Deploy Odoo Without the Manual Setup

OEC.sh automates everything in this guide — Python, PostgreSQL, Nginx, SSL, systemd, firewall, and monitoring. Deploy on any cloud provider in under 5 minutes.