How to install Odoo 19 on Ubuntu: by hand, or in a few clicks
A production-minded guide to installing Odoo 19 on an Ubuntu server: PostgreSQL, Python, wkhtmltopdf, the configuration, systemd, nginx and HTTPS. And how Cloud Buddy does all of it for you.
By Cloud Buddy · Published · 7 min read · Odoo installation, Ubuntu, Your own server
Installing Odoo on a fresh Ubuntu server takes an afternoon the first time and about an hour once you know the steps. Getting it production ready is where most of the time goes: a service that restarts on its own, a sensible number of workers, the database manager hidden from the internet, HTTPS, log rotation and backups you have actually restored once.
This guide walks through a clean installation of Odoo 19 on Ubuntu, the way Odoo's own documentation recommends (from source, in a Python virtual environment, run by systemd), with the production details that are easy to forget. At the end, you'll see how Cloud Buddy does the same thing on your server in a few clicks.
Odoo is a trademark of Odoo S.A. Cloud Buddy is an independent service, not affiliated with or endorsed by Odoo S.A.
Before you start
You need:
- An Ubuntu server you can reach as root over SSH. Ubuntu 22.04 or 24.04 with at least 2 GB of memory is a reasonable start for a small team; more users need more workers, and more workers need more memory.
- A domain name (for example
erp.example.com) whose A record points to the server's public address, if you want HTTPS. - Python 3.10 or newer. Both Ubuntu 22.04 and 24.04 ship a recent enough Python.
Everything below runs as root or with sudo.
1. System packages and PostgreSQL
Update the server, then install PostgreSQL and the libraries Odoo's Python packages are built against:
apt-get update && apt-get upgrade -y
apt-get install -y postgresql git python3-venv python3-dev build-essential \
libpq-dev libxml2-dev libxslt1-dev libldap2-dev libsasl2-dev libjpeg-dev zlib1g-devCreate a system user for Odoo and a matching PostgreSQL role that can create databases but is not a superuser:
adduser --system --group --home /opt/odoo odoo
sudo -u postgres createuser --createdb odooBecause the role has the same name as the system user, Odoo connects through PostgreSQL's local socket without a password.
2. wkhtmltopdf for PDF reports
Odoo prints invoices, quotes and every other PDF through wkhtmltopdf. Use the 0.12.6.1 build with patched Qt from the wkhtmltopdf project's releases page, not the older package in Ubuntu's archive: without the patched Qt, headers and footers are missing from your PDFs.
3. Odoo 19 in a virtual environment
Clone only the 19.0 branch (the full history is several gigabytes), then install its Python requirements into a virtual environment that belongs to Odoo:
sudo -u odoo git clone --depth 1 --branch 19.0 https://github.com/odoo/odoo.git /opt/odoo/odoo
sudo -u odoo python3 -m venv /opt/odoo/venv
sudo -u odoo /opt/odoo/venv/bin/pip install -r /opt/odoo/odoo/requirements.txt
mkdir -p /opt/odoo/custom-addons /var/log/odoo && chown odoo: /opt/odoo/custom-addons /var/log/odooYour own modules go in /opt/odoo/custom-addons. For Odoo Enterprise, you also need the Enterprise addons, which come with an Enterprise subscription from Odoo S.A., in their own folder on the addons path.
4. The configuration file
Create /etc/odoo/odoo.conf, readable by the odoo user only (chmod 640, owned by root:odoo):
[options]
admin_passwd = a-long-random-master-password
db_user = odoo
addons_path = /opt/odoo/odoo/addons,/opt/odoo/custom-addons
http_interface = 127.0.0.1
proxy_mode = True
list_db = False
workers = 3
max_cron_threads = 1
limit_memory_soft = 2147483648
limit_memory_hard = 2684354560
logfile = /var/log/odoo/odoo.logThe lines that matter most in production:
admin_passwd: the master password of the database manager. Never leave the default.list_db = False: hides the database selector and the database manager, so nobody can list, back up or drop databases from the login page.http_interface = 127.0.0.1andproxy_mode = True: Odoo only listens locally, behind nginx, and trusts nginx for the visitor's address and HTTPS.workers: with workers above zero, Odoo runs in multi-process mode. Odoo's guide suggests about (CPU cores × 2) + 1 as an upper bound, limited by memory: count roughly 150 MB for a light worker and up to 1 GB for a heavy one.
Add a logrotate rule for /var/log/odoo/odoo.log, or the log will quietly fill the disk over the months.
5. A systemd service
Create /etc/systemd/system/odoo.service:
[Unit]
Description=Odoo 19
After=network.target postgresql.service
[Service]
Type=simple
User=odoo
Group=odoo
ExecStart=/opt/odoo/venv/bin/python3 /opt/odoo/odoo/odoo-bin -c /etc/odoo/odoo.conf
Restart=on-failure
[Install]
WantedBy=multi-user.targetThen systemctl daemon-reload && systemctl enable --now odoo, and follow the log with tail -f /var/log/odoo/odoo.log until it says the server is listening.
6. nginx and HTTPS
Install nginx and certbot (apt-get install -y nginx certbot python3-certbot-nginx), then put Odoo behind nginx. With workers, the live chat and notifications go through /websocket, served by Odoo's separate gevent process on port 8072:
upstream odoo { server 127.0.0.1:8069; }
upstream odoo_ws { server 127.0.0.1:8072; }
server {
listen 80;
server_name erp.example.com;
client_max_body_size 100m;
location /websocket {
proxy_pass http://odoo_ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
location / {
proxy_pass http://odoo;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
}
}Test the configuration with nginx -t, reload nginx, then run certbot --nginx -d erp.example.com for a free Let's Encrypt certificate that renews by itself. Finally, open ports 80 and 443 in the firewall (ufw allow 'Nginx Full'), keep SSH open, and never expose 8069, 8072 or PostgreSQL's 5432 to the internet.
7. Create the database, then look after it
Open https://erp.example.com, create your database with the admin email and password, the country (it installs the right accounting localization) and the apps you need. That's the installation. The part that never ends is operations:
- Backups every day, kept somewhere other than the server, and restored once in a while to prove they work.
- Security updates for Ubuntu, and a look at who can log in over SSH.
- Monitoring: is Odoo answering, is the disk filling up, does the certificate renew?
- Upgrades to the next Odoo version before yours runs out of support.
Or: let Cloud Buddy do it on your server
Everything above is what Cloud Buddy's installer does on your own server, plus the operations afterwards. Link your server with one command, click Install Odoo, choose 19.0 (or 17.0, 18.0, 20.0), Community or Enterprise, your domain, language, country and apps, and review the plan:
- Native or Docker: native with systemd, as above, when the server's Python is recent enough; otherwise the official Odoo image in Docker.
- Sizing: workers, memory limits and cron threads computed from the server's CPU and memory and the other Odoo running there, with the math shown.
- Fixes it makes by itself: a swap file when memory is short, disk clean-up, the next free name and ports when yours are taken, and PostgreSQL, nginx and certbot when they're missing.
- Hardened by default: a random master password stored hashed, the database list off, its own system user and PostgreSQL role, and log rotation.
- HTTPS when DNS is ready: if your domain doesn't point to the server yet, Odoo opens on the server's address and the certificate is added automatically once it does.
The install takes about 8 to 15 minutes and you follow it live. If it fails, the installer removes what it created and the payment is refunded in full. Then the server gets monitoring, daily backups with weekly restore tests, a security audit and a recorded web terminal. See installing Odoo on your own server for the details, and the pricing page for the current prices.