Skip to content

TutorialsBusiness apps

How to install Odoo 20 Community with Docker Compose and HTTPS

Install Odoo 20 Community from the official Docker image with PostgreSQL on Ubuntu or Debian, then add Caddy HTTPS, websocket routing, workers and backups.

  • Intermediate
  • 45 min read
  • Updated

Tested on: Ubuntu 24.04 LTS, Ubuntu 26.04 LTS, Debian 12, Debian 13

This guide is not available in your language yet, so it is shown in English.

On this page
  1. Prerequisites
  2. Step 1 — Create the project folder and secrets
  3. Step 2 — Write odoo.conf
  4. Step 3 — Write the Compose file
  5. Step 4 — Create the database role and start Odoo
  6. Step 5 — Create the database, then close the database manager
  7. Step 6 — Publish Odoo over HTTPS with websocket routing
  8. Step 7 — Install apps and custom modules
  9. Back up and restore
  10. Update Odoo
  11. Troubleshooting
  12. Odoo exits with a message that the postgres user is a security risk
  13. Discuss, notifications or live chat do not update in real time
  14. The login page shows no database or a database not found error
  15. Requests time out or workers are restarted with memory limit messages
  16. Links in emails use http:// or an internal address
  17. Next steps

Odoo is a suite of open-source business applications: CRM, sales, invoicing, inventory, projects, website, e-commerce and many more, all sharing one PostgreSQL database. This guide installs Odoo 20.0 Community Edition, the current stable release (Odoo's nightly server lists it as released in September 2026), with the official odoo Docker image and a PostgreSQL 17 container. You configure odoo.conf for production with workers, proxy mode, a master password and a database filter, publish Odoo through Caddy with HTTPS and correct websocket routing, close the database manager, and set up backups and updates.

Prerequisites

  • A server running Ubuntu 24.04 LTS, Ubuntu 26.04 LTS, Debian 12 or Debian 13 with Docker Engine and the Compose plugin: Docker on Ubuntu or Docker on Debian. The commands assume your user is in the docker group; otherwise prefix them with sudo.
  • A non-root user with sudo rights and SSH key login: Secure a new Linux server and Set up SSH keys.
  • A domain name such as odoo.example.com with an A record (and optionally an AAAA record) pointing at the server, and Caddy from Caddy reverse proxy.

Odoo does not publish minimum hardware requirements. Its deployment guide offers a sizing method instead: at most (number of CPUs × 2) + 1 workers, about six concurrent users per worker, and RAM of roughly workers × (0.8 × 150 MB + 0.2 × 1 GB), assuming 80% light and 20% heavy requests. The figures below are a conservative starting point for a small team, not official numbers:

ResourceMinimum (official)Suggested starting point
CPUNot published2 vCPU
RAMNot published4 GB
DiskNot published40 GB plus your attachments

On the software side, Odoo 20 requires PostgreSQL 16 or newer. The examples on the image's Docker Hub page still use postgres:15, so this guide pins postgres:17 instead.

Step 1 — Create the project folder and secrets

Keep the stack in /opt/odoo, with a config folder for odoo.conf and an addons folder for custom modules. Because odoo.conf will hold the master password, close the project folder to other users:

Bash
sudo mkdir -p /opt/odoo/config /opt/odoo/addons
sudo chown -R $USER:$USER /opt/odoo
chmod 700 /opt/odoo
cd /opt/odoo

Generate three different secrets: one for the PostgreSQL superuser, one for the odoo database role, and one for Odoo's master password:

Bash
openssl rand -hex 24
openssl rand -hex 24
openssl rand -hex 24

Create /opt/odoo/.env with nano .env and paste the first two values:

.env
POSTGRES_PASSWORD=paste-the-first-value
ODOO_DB_PASSWORD=paste-the-second-value
Bash
chmod 600 .env

Step 2 — Write odoo.conf

Create /opt/odoo/config/odoo.conf with nano config/odoo.conf. Paste the third secret as admin_passwd:

INI
[options]
addons_path = /mnt/extra-addons
data_dir = /var/lib/odoo
admin_passwd = paste-the-third-value
proxy_mode = True
dbfilter = ^odoo$
list_db = True
workers = 4
max_cron_threads = 1
limit_memory_soft = 629145600
limit_memory_hard = 1677721600
limit_time_cpu = 600
limit_time_real = 1200
limit_request = 8192

What these settings do:

  • admin_passwd is the master password that protects the database management screens. Odoo's documentation says to replace the default with a randomly generated value.
  • proxy_mode = True makes Odoo read the X-Forwarded-* headers from Caddy, so it knows requests arrive over HTTPS. Odoo warns never to enable it without a reverse proxy in front.
  • dbfilter = ^odoo$ serves only a database named odoo, whatever hostname is used.
  • list_db = True is temporary. You need the database manager once in Step 5 and then turn it off.
  • workers = 4 switches Odoo from the threaded development server to the multi-processing server meant for production. With workers enabled, Odoo also starts a separate websocket process on port 8072 (the --gevent-port default) for live chat and real-time notifications.
  • The limit_ values come from the sample configuration in Odoo's deployment guide. Workers above limit_memory_soft are recycled after the current request; workers above limit_memory_hard are killed at once.

Adjust workers to your server with the formula from the Prerequisites, and leave room for PostgreSQL.

Step 3 — Write the Compose file

Create /opt/odoo/compose.yaml:

YAML
services:
  db:
    image: postgres:17
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - odoo-db-data:/var/lib/postgresql/data
    restart: unless-stopped

  odoo:
    image: odoo:20.0
    depends_on:
      - db
    environment:
      HOST: db
      USER: odoo
      PASSWORD: ${ODOO_DB_PASSWORD}
    ports:
      - "127.0.0.1:8069:8069"
      - "127.0.0.1:8072:8072"
    volumes:
      - odoo-web-data:/var/lib/odoo
      - ./config:/etc/odoo
      - ./addons:/mnt/extra-addons
    restart: unless-stopped

volumes:
  odoo-db-data:
  odoo-web-data:

The image reads /etc/odoo/odoo.conf, stores the filestore and sessions in /var/lib/odoo, and loads custom modules from /mnt/extra-addons. Its entrypoint passes HOST, USER and PASSWORD to Odoo as the database connection. PostgreSQL runs as its own superuser postgres; Odoo connects as a separate odoo role, because Odoo refuses to connect as postgres and its deployment guide says the database user should not be a superuser. Both ports are bound to 127.0.0.1, since Docker-published ports bypass ufw.

The 20.0 tag follows rebuilds of the 20.0 series. Docker Hub also lists dated tags such as 20.0-20260926 if you want to pin one exact build.

Step 4 — Create the database role and start Odoo

Start PostgreSQL alone, then create the odoo role with the flags Odoo's documentation uses: allowed to create databases, but not a superuser and not allowed to create roles. Paste ODOO_DB_PASSWORD from .env twice when prompted:

Bash
docker compose up -d db
docker compose exec db createuser -U postgres --createdb --no-createrole --no-superuser --pwprompt odoo

If createuser reports that it cannot connect, PostgreSQL is still initialising; wait a few seconds and run it again. Then start Odoo and follow its log:

Bash
docker compose up -d
docker compose ps
docker compose logs -f odoo

Stop following with Ctrl+C once the log shows the HTTP service running on port 8069. Check that the database manager answers locally:

Bash
curl -I http://127.0.0.1:8069/web/database/manager

You should get HTTP/1.1 200 OK.

Step 5 — Create the database, then close the database manager

Create the database through an SSH tunnel, so the database manager is never exposed to the internet. On your own computer, open a tunnel to the server:

Bash
ssh -L 8069:127.0.0.1:8069 admin@203.0.113.10

Keep that session open and browse to http://localhost:8069/web/database/manager. Choose Create Database and enter:

  • Master Password: the admin_passwd value from odoo.conf.
  • Database Name: odoo, so it matches dbfilter.
  • Email and Password: the login of your first administrator. Use a long, unique password.
  • Your language and country. Leave Demo data unticked on a production database.

When Odoo logs you in, the database exists. Back on the server, switch the database manager off and restart Odoo:

Bash
cd /opt/odoo
sed -i 's/^list_db = True/list_db = False/' config/odoo.conf
grep list_db config/odoo.conf
docker compose restart odoo

grep should print list_db = False. The database selection and management screens are now blocked, which is what Odoo's documentation recommends for internet-facing systems. Close the SSH tunnel.

Step 6 — Publish Odoo over HTTPS with websocket routing

With workers enabled, normal HTTP traffic goes to port 8069 and websocket connections on /websocket go to port 8072. Odoo's deployment guide shows this split for Nginx; the Caddy equivalent uses two handle blocks. Add this site to /etc/caddy/Caddyfile:

Caddyfile
odoo.example.com {
    encode zstd gzip

    handle /websocket* {
        reverse_proxy 127.0.0.1:8072
    }

    handle {
        reverse_proxy 127.0.0.1:8069
    }
}

Caddy upgrades websocket connections and sets X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host on its own, which is what proxy_mode expects. Reload Caddy and allow only SSH and web traffic:

Bash
sudo systemctl reload caddy
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

Open https://odoo.example.com and log in with the administrator account from Step 5. To check the websocket route, open your browser's developer tools on the Network tab: a request to /websocket should show status 101. If you prefer Nginx, follow the sample configuration in Odoo's deployment guide together with Nginx with Certbot.

Odoo emails user invitations, password resets, notifications and documents such as quotations and invoices through the outgoing mail server you add, with developer mode active, under Settings → Technical → Email: Outgoing Mail Servers (host smtp.example.com, port 587, TLS (STARTTLS) encryption, your SMTP user and password; use Test Connection before saving).

Step 7 — Install apps and custom modules

Install Odoo's own apps from the Apps menu. Third-party or in-house modules go into /opt/odoo/addons, one folder per module, readable by the container. After copying a module, restart Odoo:

Bash
docker compose restart odoo

Then activate developer mode in Settings, choose Apps → Update Apps List, and install the module. Only install modules from sources you trust; a module runs with full access to your data.

Back up and restore

Odoo's state is split in two: the PostgreSQL database and the filestore (attachments and other files) under /var/lib/odoo/filestore/odoo. A backup needs both, taken at the same time, plus your configuration. The database manager's backup button produces such a bundle, but you switched it off in Step 5; the commands below give you the same content from the shell:

Bash
sudo mkdir -p /opt/backups
sudo chown $USER:$USER /opt/backups
chmod 700 /opt/backups
cd /opt/odoo
docker compose exec -T db pg_dump -U postgres -Fc odoo > /opt/backups/odoo-db-$(date +%F).dump
docker compose exec -T odoo tar czf - -C /var/lib/odoo filestore/odoo > /opt/backups/odoo-filestore-$(date +%F).tar.gz
tar czf /opt/backups/odoo-config-$(date +%F).tar.gz -C /opt/odoo compose.yaml .env config addons

pg_dump takes a consistent snapshot while Odoo keeps running. To restore, stop Odoo, recreate the database, load the dump and unpack the filestore into the volume. Replace the dates with those of your backup:

Bash
cd /opt/odoo
docker compose stop odoo
docker compose exec -T db dropdb -U postgres --if-exists --force odoo
docker compose exec -T db createdb -U postgres --owner=odoo odoo
docker compose exec -T db pg_restore -U postgres -d odoo < /opt/backups/odoo-db-2026-10-09.dump
docker compose run --rm --no-deps -T odoo tar xzf - -C /var/lib/odoo < /opt/backups/odoo-filestore-2026-10-09.tar.gz
docker compose up -d

On a new server, unpack the configuration archive into /opt/odoo first, then run Step 4 to start PostgreSQL and create the odoo role, and only then run the restore commands. Copy every backup off the server and test a restore regularly.

Update Odoo

Updates within 20.0 bring bug and security fixes. Back up first, then pull the rebuilt image and recreate the container:

Bash
cd /opt/odoo
docker compose pull
docker compose up -d
docker compose logs -f odoo

If a fix changes module data and you see errors about missing fields or views afterwards, update all modules of the database once. Odoo is stopped while this runs:

Bash
docker compose stop odoo
docker compose run --rm odoo odoo -d odoo -u all --stop-after-init
docker compose up -d

Major upgrades, such as 19.0 to 20.0 or 20.0 to a later release, are a different job. Odoo's Docker Hub page warns that moving between major versions needs elaborate migration scripts. Plan the database migration with Odoo's upgrade documentation, port your custom modules, and test the whole process on a copy of the database before you change the image tag. Never change odoo:20.0 to a new major and simply restart. Keep the PostgreSQL major version in postgres:17 unchanged as well; a new PostgreSQL major needs a dump and restore.

Troubleshooting

Odoo exits with a message that the postgres user is a security risk

Odoo is configured to connect as postgres, which it refuses. Check that USER: odoo is set in compose.yaml, that the role exists (docker compose exec db psql -U postgres -c "\du"), and that odoo.conf has no db_user = postgres line.

Discuss, notifications or live chat do not update in real time

Websocket requests are not reaching the websocket process. Check that Caddy sends /websocket* to 127.0.0.1:8072, that port 8072 is published in compose.yaml, that workers is above 0, and that proxy_mode = True is set. Reload Caddy and restart Odoo after each change.

The login page shows no database or a database not found error

dbfilter does not match the database name. With dbfilter = ^odoo$ the database must be called exactly odoo. Fix the filter in odoo.conf or recreate the database with the matching name, then restart Odoo.

Requests time out or workers are restarted with memory limit messages

A worker exceeded limit_time_cpu, limit_time_real or the memory limits. Read the details with docker compose logs odoo, raise the specific limit if long reports or imports legitimately need it, and reduce workers or add RAM if the server itself runs out of memory.

Odoo builds links from the web.base.url system parameter. Make sure proxy_mode = True is set, then log in as an administrator through https://odoo.example.com and check the parameter under Settings → Technical → System Parameters in developer mode.

Next steps

Frequently asked questions

Where does Odoo keep attachments and uploaded files?

In the filestore, /var/lib/odoo/filestore/odoo inside the odoo-web-data volume, not in PostgreSQL. A backup or a move to another server needs the database dump and the filestore archive taken at the same time, as shown in the backup section.

Why does this guide use Docker instead of the deb packages?

Odoo's packaging page says the Odoo 20 deb package currently supports Ubuntu 24.04 only, and wkhtmltopdf 0.12.6 must then be installed by hand for PDF headers and footers. The official image, maintained by Odoo, runs on every Docker-supported release and already contains wkhtmltopdf 0.12.6.1.

How many workers should I configure?

Odoo's deployment guide gives (number of CPUs x 2) + 1 as a rule of thumb for the maximum, estimates about six concurrent users per worker, and suggests budgeting roughly 150 MB of RAM for light requests and 1 GB for heavy ones. Start lower on small servers and leave memory for PostgreSQL.

Can I upgrade from Odoo 19 to 20 by changing the image tag?

No. A major version change needs a database migration, and custom modules must be ported to the new version as well. Plan it with Odoo's upgrade documentation and always test the upgrade on a copy of the database first.

Should I keep the database manager enabled?

No. Odoo's documentation strongly advises disabling it on internet-facing systems. Set list_db = False once your database exists and use pg_dump plus a filestore archive for backups instead of the web backup button.

Which HyperDC servers can run Odoo?

Any HyperDC Linux VPS, VDS or dedicated server with root access where Docker Engine is supported. Odoo does not publish minimum hardware, so size the server with the worker formula in this guide and grow it as your users and modules grow.

Sources

Gerar senha

Please confirm