How To Install Odoo on Ubuntu 26.04 LTS

Install Odoo on Ubuntu 26.04

Installing Odoo on Ubuntu 26.04 LTS is straightforward on paper, but the difference between a quick lab setup and a stable production deployment is huge. The package may install cleanly, yet real-world usage quickly exposes the details that matter: PostgreSQL tuning, reverse proxy behavior, worker allocation, SSL handling, log management, and the small configuration choices that determine whether the system stays fast under load or becomes the source of a late-night incident. Odoo’s official documentation confirms that Debian-based systems are supported through packaged installers and that the recommended approach on Linux is to install PostgreSQL locally, then deploy Odoo through the nightly repository or package installer.

Ubuntu 26.04 LTS is a sensible base for Odoo because it brings a modern kernel, updated libraries, and a long support window, which matters when the ERP system is expected to run for years without constant rebuilds. The catch is that Odoo is not a “just install and forget” application. It is a Python-based web app with database-heavy behavior, browser-driven workflows, scheduled jobs, file attachments, and report generation, which means you need to think like an administrator, not just a package consumer. The result should be a system that boots on time, survives traffic spikes, and remains easy to troubleshoot when someone complains that invoices are slow or a module update broke their workflow. Odoo’s documentation also notes that the default package expects PostgreSQL on the same host, which is important when planning the architecture.

This guide is written for the way production servers actually behave. It covers the full path from system preparation to service hardening, Nginx reverse proxying, SSL, and practical troubleshooting. It also assumes you want a setup that is maintainable, readable, and sane at 3 a.m., which is usually where “simple” deployments start to show their flaws.

Prerequisites

Before touching Odoo, make sure the server itself is ready. Ubuntu 26.04 LTS should be fully patched, reachable over SSH, and configured with a non-root administrative user. For a small deployment, 2 CPU cores and 4 GB RAM is the bare minimum; for a production environment, 4 CPU cores and 8 GB RAM is a more realistic starting point, especially if you expect multiple users, scheduled actions, or report generation. Disk performance matters more than people expect, because Odoo and PostgreSQL both punish slow storage.

A clean hostname and DNS record are also worth getting right early. If the server will be exposed publicly, point a domain such as odoo.example.com to the machine before configuring HTTPS. You will also want to decide whether this instance is for production, staging, or internal use, because that affects firewall policy, database exposure, and backup routines. For production, keep PostgreSQL private, bind Odoo to localhost, and expose only Nginx on ports 80 and 443.

Update the system

Start with a full update so the base system is current and any old package conflicts are dealt with before Odoo enters the picture.

sudo apt update
sudo apt upgrade -y
sudo apt install -y ca-certificates curl gnupg lsb-release software-properties-common

That first update does two things. It reduces the chance of dependency surprises, and it ensures the machine is in a known-good state before adding external repositories. On servers I’ve seen go sideways, the root cause often wasn’t Odoo at all; it was a half-updated OS with stale packages, missing certificates, or odd Python/library mismatches.

Create the service user

Odoo should run under its own unprivileged system account. This is standard practice, but it is still too often skipped in hurried deployments.

sudo adduser --system --home=/opt/odoo --group --shell /bin/bash odoo
sudo mkdir -p /var/lib/odoo
sudo chown -R odoo:odoo /var/lib/odoo /opt/odoo

Using a dedicated user limits damage if the application is compromised and keeps file ownership predictable. It also makes troubleshooting easier because logs, addons, and custom modules can be organized cleanly under one account. If you later add custom code or scheduled exports, the permissions model remains obvious instead of becoming a tangle of root, www-data, and ad hoc file copies.

Install PostgreSQL

Odoo relies heavily on PostgreSQL, so this is not optional. The official Odoo documentation for Linux explicitly recommends installing PostgreSQL and, for the packaged Linux installer, expects the database server on the same host by default.

sudo apt install -y postgresql postgresql-contrib
sudo systemctl enable --now postgresql
sudo -u postgres createuser --createdb --no-createrole --no-superuser odoo

That createuser command creates a PostgreSQL role for Odoo with database creation rights but without elevated superuser privileges. That balance matters. Odoo needs enough access to create and manage databases, but not so much access that a compromised app could own the entire database server. In production, keep PostgreSQL listening only on localhost unless you have a deliberate split-tier architecture.

If you plan a larger installation, also review PostgreSQL’s memory settings, checkpoint behavior, and WAL retention. Odoo can create bursty load during module upgrades, batch imports, and report generation. PostgreSQL defaults are safe, but they are not always optimal.

Add the Odoo repository

Odoo’s official Linux installer documentation shows a supported method using the nightly repository and a signed keyring on Debian-based distributions.

wget -q -O - 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/19.0/nightly/deb/ ./'
  | sudo tee /etc/apt/sources.list.d/odoo.list
sudo apt update

I’m using the 19.0 repository here because the official docs currently publish package instructions for that release line and Ubuntu/Debian-based systems. If your production policy requires a different Odoo major version, pin it explicitly rather than relying on whatever happens to be newest when the server is built. ERP systems are not the place to be casual about version drift.

Install Odoo

Now install the package.

sudo apt install -y odoo

After installation, confirm the service exists and is enabled.

systemctl status odoo --no-pager
systemctl enable odoo

On a well-behaved install, Odoo should come up as a managed systemd service. That is exactly what you want. Systemd gives you restart control, journal logging, boot integration, and a sane operational model when things go wrong. Avoid the temptation to run Odoo manually in a terminal for anything beyond testing.

Install required tools

You will almost always need a few additional packages for day-to-day administration, report generation, and web serving.

sudo apt install -y nginx fontconfig xfonts-75dpi wkhtmltopdf

The wkhtmltopdf part deserves attention. Odoo uses it for PDF generation in many setups, and the version matters because upstream Odoo notes that the standard Debian/Ubuntu repository package may not support headers and footers properly; a patched build is often required depending on the Odoo version and your reporting needs. In practice, this is one of the first places where users notice “it works, but not really” behavior: invoices render oddly, page numbers disappear, or PDFs look fine on one server and broken on another.

If your installed Odoo version has a stronger requirement for a particular wkhtmltopdf build, follow the vendor’s compatibility guidance rather than assuming the distro package is enough. Report rendering is a business-critical feature, not a cosmetic extra.

Configure Odoo

The packaged installer usually places the main configuration file under /etc/odoo. Confirm the file path on your server, then edit it carefully.

sudo nano /etc/odoo/odoo.conf

A practical baseline configuration looks like this:

[options]
admin_passwd = change-this-master-password
db_host = False
db_port = False
db_user = odoo
db_password = False
addons_path = /usr/lib/python3/dist-packages/odoo/addons
data_dir = /var/lib/odoo
xmlrpc_port = 8069
logfile = /var/log/odoo/odoo.log
proxy_mode = True

A few notes matter here. The admin_passwd value is the master password used for database management operations, and it should be long and unique. proxy_mode = True is important when Odoo sits behind Nginx, because it helps Odoo interpret forwarded headers correctly and generate the right URLs. The exact addons_path can differ depending on package layout, so confirm the installed package structure on the machine rather than blindly copying paths from a random blog post.

Create the log directory and set ownership.

sudo mkdir -p /var/log/odoo
sudo chown -R odoo:odoo /var/log/odoo /var/lib/odoo
sudo chmod 640 /etc/odoo/odoo.conf
sudo chown odoo:odoo /etc/odoo/odoo.conf

Set up Nginx

For production, do not expose Odoo directly to the internet. Put Nginx in front of it. That gives you TLS termination, better buffering, cleaner logging, and the option to apply security headers and rate limits at the edge.

Create a site file:

sudo nano /etc/nginx/sites-available/odoo

Use a configuration like this:

upstream odoo_backend {
    server 127.0.0.1:8069;
}

server {
    listen 80;
    server_name odoo.example.com;

    access_log /var/log/nginx/odoo.access.log;
    error_log /var/log/nginx/odoo.error.log;

    client_max_body_size 100m;
    proxy_read_timeout 720s;
    proxy_connect_timeout 720s;
    proxy_send_timeout 720s;

    location / {
        proxy_pass http://odoo_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_redirect off;
    }

    location ~* /web/static/ {
        expires 30d;
        proxy_cache_valid 200 30m;
        proxy_pass http://odoo_backend;
    }
}

Enable it and test the syntax:

sudo ln -s /etc/nginx/sites-available/odoo /etc/nginx/sites-enabled/odoo
sudo nginx -t
sudo systemctl reload nginx

Nginx matters for more than just “making HTTPS work.” It also protects Odoo from direct exposure, gives you a cleaner place to handle timeouts, and makes it easier to cache static assets. Under load, those details reduce noise and improve responsiveness.

Enable HTTPS

For public deployments, SSL is non-negotiable. The simplest route is Let’s Encrypt using Certbot.

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d odoo.example.com

Certbot can update the Nginx config automatically and set up redirects from HTTP to HTTPS. After that, verify renewal works:

sudo certbot renew --dry-run

If you are running a private/internal Odoo instance, a self-signed certificate may be acceptable temporarily, but it is still better to use trusted internal PKI if you have it. Browser warnings are more than cosmetic; they teach users to ignore security signals, which is a bad habit to build around business software.

Open the firewall

Only expose the ports you actually need. On Ubuntu, UFW is usually enough for a standard deployment.

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status verbose

You should not need to open port 8069 to the public if Nginx is fronting Odoo on the same server. That port can stay local-only. If you later decide to put a load balancer in front of Nginx or terminate TLS upstream, revisit the network path deliberately instead of punching holes until it works.

Start and verify

Bring the services up and confirm everything is listening where it should.

sudo systemctl restart postgresql
sudo systemctl restart odoo
sudo systemctl restart nginx
sudo ss -tulpn | grep -E '8069|80|443'

Check the logs if something fails:

sudo journalctl -u odoo -n 100 --no-pager
sudo tail -n 100 /var/log/odoo/odoo.log
sudo tail -n 100 /var/log/nginx/odoo.error.log

If Odoo loads in the browser and asks to create a database, the core stack is working. That is the easy part. The real test is whether the instance stays stable after you install modules, import data, run scheduled jobs, and let a few users abuse it for a week.

Performance tuning

Performance tuning is where production setups earn their keep. Odoo can run acceptably with defaults on a small system, but once users start multitasking inside the app, latency comes from a handful of predictable places: PostgreSQL, Python workers, disk I/O, and proxy configuration.

Start with worker mode only when your server can support it. For small installs, a single process is fine. For real production use, configure workers based on CPU cores and available RAM. A common rule of thumb is workers = (CPU * 2) + 1, but only if memory headroom is sufficient. Also consider longpolling and memory limits, because an aggressive worker count on an undersized box can make Odoo slower, not faster.

Useful optimizations include:

  • Put the database on fast SSD storage.
  • Keep PostgreSQL on the same machine for small and medium deployments.
  • Tune shared_buffers, work_mem, and autovacuum carefully rather than wildly.
  • Increase proxy_read_timeout if large reports or imports are involved.
  • Use proxy_mode = True so Odoo handles forwarded headers correctly.
  • Enable gzip at the reverse proxy for static content.
  • Set sane client_max_body_size values to allow imports and attachments.

For disk-heavy environments, storage latency often becomes the invisible bottleneck. If invoice generation is slow and the server load looks “fine,” check I/O wait before touching Python settings. On busy systems, this distinction saves a lot of wasted time.

Security hardening

A production Odoo server should not feel exposed. Keep PostgreSQL local, use a non-root service account, and avoid running extra services on the same host unless there is a reason. Apply regular security updates and keep an eye on the Odoo and PostgreSQL release notes, especially if the instance is internet-facing.

A few sensible hardening steps:

  • Use a strong master password for Odoo database management.
  • Restrict SSH access with keys and, ideally, a firewall allowlist.
  • Enable unattended security updates if your change management allows it.
  • Store backups off the server.
  • Limit file permissions on configuration files and private keys.
  • Review installed modules periodically, especially third-party addons.

In the real world, the biggest risk is rarely the base package. It is usually a weak password, a forgotten test database, an exposed admin port, or a plugin someone installed months ago and never reviewed again. ERP platforms accumulate trust, and that trust needs regular maintenance.

Backups and recovery

Backups should include both PostgreSQL and the Odoo filestore. The database alone is not enough, because attachments and binary content are often stored outside the relational tables. A good backup routine captures the database dump, the filestore, and the configuration files required to restore the instance quickly.

A simple backup flow might look like this:

sudo -u postgres pg_dump odoo_db_name > /backup/odoo_db_name.sql
sudo tar -czf /backup/odoo_filestore.tar.gz /var/lib/odoo
sudo cp /etc/odoo/odoo.conf /backup/

Test restoration regularly. Backups that were never restored are a hope, not a strategy. If Odoo is tied to sales, accounting, or operations, recovery time matters just as much as recovery completeness.

Common problems

Odoo does not start

Check the service status and logs first.

sudo systemctl status odoo --no-pager
sudo journalctl -u odoo -xe --no-pager

Typical causes include bad config syntax, wrong file ownership, missing Python dependencies, or PostgreSQL authentication issues. If you recently edited odoo.conf, one misplaced character can stop the service completely.

Port 8069 is busy

Another process may already be using the Odoo port.

sudo ss -ltnp | grep 8069

Change the port in the config if needed, but on a standard single-instance deployment, 8069 is usually the correct default. Port conflicts often happen when a previous test instance was left running or a manual process survived a reboot.

Login works, but page loading is broken

That often points to reverse proxy headers or HTTPS misconfiguration. Confirm that Nginx is passing X-Forwarded-Proto, Host, and related headers, and that proxy_mode is enabled in Odoo. Without those pieces, Odoo may generate incorrect URLs or mixed-content behavior that breaks the frontend in subtle ways.

PDFs fail or render badly

This is usually a wkhtmltopdf compatibility problem. Odoo’s own documentation and related guidance point out that the package version matters and that the distro-provided build may not support all reporting features correctly. If invoices or reports look wrong, verify the exact binary version before blaming templates or modules.

Database creation fails

Check the Odoo master password and PostgreSQL role. Also verify that the service can connect to the database backend using the credentials defined in the configuration. On misconfigured systems, the error looks like an app issue but the root cause is usually database authentication.

Real-world scenarios

A small company with ten users usually cares about uptime and PDF output first. In that case, a simple single-server install with Nginx, local PostgreSQL, and solid backups is often the best choice. It is easy to manage, easy to secure, and easy to recover when a module update goes wrong.

A larger operation with dozens of users may need a more deliberate layout. You might separate the database server, add more CPU and memory, or place a caching layer in front of the web tier. Traffic spikes also matter during accounting close, end-of-month reporting, or import-heavy workflows. Those are the moments when worker tuning and storage latency show their true value.

In my experience, the servers that feel “slow” are often not under constant pressure at all. They are usually fine most of the day, then choke when one heavy action lands: a batch import, a mass email send, a PDF export, or a cron job that wakes up at the wrong moment. Planning for those spikes is what separates a lab install from a dependable production system.

Best practices

Keep the installation boring. That sounds dull, but boring systems are easy to own.

  • Use official or documented package sources whenever possible. Odoo’s package documentation provides the supported Debian/Ubuntu repository method.
  • Put Nginx in front of Odoo and keep the app bound to localhost.
  • Monitor disk, memory, and PostgreSQL health with tools such as htop, iotop, vmstat, free, and journalctl.
  • Back up the database and filestore together.
  • Review module updates in staging before production.
  • Keep custom addons version-controlled.
  • Treat SSL certificates and renewal checks as operational tasks, not one-time setup work.

If the system becomes mission-critical, add monitoring around service status, disk usage, and response times. A simple alert on Odoo being down is good. An alert on PostgreSQL growing too fast or disk I/O saturating before a failure is better.

r00t is a Linux Systems Administrator and open-source advocate with over ten years of hands-on experience in server infrastructure, system hardening, and performance tuning. Having worked across distributions such as Debian, Arch, RHEL, and Ubuntu, he brings real-world depth to every article published on this blog. r00t writes to bridge the gap between complex sysadmin concepts and practical, everyday application — whether you are configuring your first server or optimizing a production environment. Based in New York, US, he is a firm believer that knowledge, like open-source software, is best when shared freely.

Related Posts