← Back to guides

Odoo deployment

How to host Odoo on a subdomain with HTTPS

This guide shows how to expose a self-hosted Odoo instance safely through a dedicated subdomain using DNS, Nginx, HTTPS, proxy headers, websocket routing, and database manager protection.

Target architecture

Odoo should stay private on local ports. Public traffic should reach Nginx first, and Nginx should proxy trusted requests to Odoo.

Browser
  → https://odoo.example.com
  → Nginx on 80/443
  → 127.0.0.1:8069 Odoo
  → 127.0.0.1:8072 Odoo websocket
  → private PostgreSQL

Read this before changing Nginx or DNS

This guide assumes the server has already been and Odoo is already running locally. Do not expose Odoo directly on just to make it reachable. Keep Odoo local, put in front, and issue for the subdomain.
Before changing production , keep an active open, confirm you have , and make sure you have a or .

Planning

Design decisions to make before changing DNS, Nginx, HTTPS, or public access.

DNS change

Domain records that affect where the subdomain points. DNS may take time to propagate.

Safe check

Read-only commands or manual checks that should not change the server state.

Caution

Checks that may reveal secrets or require careful interpretation before taking action.

Change required

Actions that can affect DNS, HTTPS, Nginx, access, or production availability. Plan before applying.

Deployment checklist

1. Choose a dedicated Odoo subdomain

Planning

Odoo should be exposed through a clean HTTPS subdomain instead of directly through port 8069. This keeps the public entry point controlled by Nginx.

Show command and interpretation

Command or manual check

Recommended example:

odoo.example.com

Avoid exposing Odoo directly through:
http://your-server-ip:8069

How to interpret the result

The public URL should be a normal HTTPS domain.

Good:
- https://odoo.example.com

Avoid:
- http://your-server-ip:8069
- http://your-server-ip:8072
- public Docker bindings such as 0.0.0.0:8069

2. Create the DNS record

DNS change

The subdomain must point to the public IP address of the server that will host Odoo before Certbot can issue an HTTPS certificate.

Show command and interpretation

Command or manual check

# In your DNS provider, create:

Type: A
Name: odoo
Value: YOUR_SERVER_PUBLIC_IP
TTL: Auto or default

# Then verify from your computer or server:
dig +short odoo.example.com

# Optional: compare with public DNS resolvers
dig @8.8.8.8 +short odoo.example.com
dig @1.1.1.1 +short odoo.example.com

# If the public DNS resolvers return the right IP
# but the local server still returns nothing or an old value:
sudo resolvectl flush-caches
sudo systemctl restart systemd-resolved

# Then test again:
dig +short odoo.example.com

How to interpret the result

The command should return the public IP address of the server that will host Odoo.

Good result:
- YOUR_SERVER_PUBLIC_IP

If it returns nothing, the old IP, or a different IP:
- wait for DNS propagation
- confirm the DNS zone is the correct one
- confirm there is no conflicting CNAME or A record
- confirm you are editing the DNS provider actually used by the domain
- compare local DNS with public resolvers such as 8.8.8.8 and 1.1.1.1

If public resolvers return the correct IP but the server still returns nothing, the server may have a stale local DNS cache. Flushing systemd-resolved can fix this on Ubuntu-based servers.

If Odoo is hosted on an office/on-premise server instead of a VPS:
- the public IP must route to that server or firewall
- ports 80 and 443 must be forwarded to the reverse proxy
- the server should still avoid exposing Odoo 8069, Odoo 8072, or PostgreSQL 5432 directly

3. Confirm Odoo is only listening locally

Safe check

Before exposing Odoo through Nginx, confirm Odoo and PostgreSQL are not already exposed directly to the internet.

Show command and interpretation

Command or manual check

sudo ss -tulpn | grep -E ":(8069|8072|5432)"

docker ps --format "table {{.Names}}\t{{.Ports}}"

How to interpret the result

Safer examples:
- 127.0.0.1:8069
- 127.0.0.1:8072
- PostgreSQL internal Docker network only
- PostgreSQL on 127.0.0.1 only

Risky examples:
- 0.0.0.0:8069
- 0.0.0.0:8072
- 0.0.0.0:5432
- 0.0.0.0:8069->8069/tcp in Docker

If Odoo is already public, fix the binding before continuing.

4. Confirm local Odoo responds

Safe check

Nginx should proxy to a working local Odoo service. Test Odoo locally before debugging DNS or HTTPS.

Show command and interpretation

Command or manual check

curl -I http://127.0.0.1:8069/

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

How to interpret the result

The local Odoo homepage may return 200, 303, or another normal Odoo response.

The database manager route may exist locally. That is not automatically dangerous if Odoo is local-only.

The important goal is:
- local Odoo works
- public direct port 8069 is not exposed
- public access will go through Nginx only

5. Create the Nginx reverse proxy config

Change required

Nginx will become the public HTTPS entry point and forward traffic to local Odoo services.

Show command and interpretation

Command or manual check

sudo nano /etc/nginx/sites-available/odoo.example.com

How to interpret the result

Create a dedicated Nginx site for the Odoo subdomain.

Do not put Odoo inside the main website or application Nginx config unless you intentionally want them coupled.

Use a separate server block so Odoo can be managed, secured, tested, and disabled independently from the main application.

6. Example Nginx config for Odoo

Change required

This config proxies normal Odoo traffic to 8069, websocket traffic to 8072, and blocks sensitive database manager routes publicly.

Show command and interpretation

Command or manual check

server {
    listen 80;
    listen [::]:80;

    server_name odoo.example.com;

    client_max_body_size 100M;

    # Certbot will later replace this with HTTPS redirects/config.
    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;

        proxy_buffering off;
    }

    # Odoo websocket / longpolling
    location /websocket {
        proxy_pass http://127.0.0.1:8072;

        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;

        proxy_read_timeout 720s;
        proxy_connect_timeout 720s;
        proxy_send_timeout 720s;
    }

       # Extra protection for sensitive Odoo database manager routes
    location ~* ^/web/database/(manager|selector|create|drop|backup|restore|duplicate) {
        return 403;
    }
}

How to interpret the result

Important points:
- public traffic goes to Nginx
- Nginx proxies Odoo to 127.0.0.1:8069
- websocket traffic goes to 127.0.0.1:8072
- sensitive /web/database routes are blocked publicly
- proxy headers are set so Odoo understands HTTPS and client IPs
- client_max_body_size allows normal Odoo uploads, imports, and attachments without hitting Nginx's small default upload limit

This assumes Odoo has proxy_mode = True in odoo.conf.

7. Enable the Nginx site

Change required

After creating the config, enable it with a symlink and test Nginx before reloading.

Show command and interpretation

Command or manual check

sudo ln -s /etc/nginx/sites-available/odoo.example.com /etc/nginx/sites-enabled/odoo.example.com

sudo nginx -t

sudo systemctl reload nginx

How to interpret the result

Expected:
- nginx -t returns successful
- reload does not disconnect SSH
- http://odoo.example.com reaches Odoo or redirects later after HTTPS setup

If nginx -t fails, do not reload until the syntax error is fixed.

8. Issue HTTPS certificate with Certbot

Change required

HTTPS should be enabled before real users log in to Odoo. Certbot can configure Nginx automatically.

Show command and interpretation

Command or manual check

sudo certbot --nginx -d odoo.example.com

How to interpret the result

Certbot should:
- verify DNS points to the VPS
- issue a certificate
- update Nginx config for HTTPS
- optionally redirect HTTP to HTTPS

If Certbot fails:
- confirm DNS resolves to the VPS
- confirm ports 80 and 443 are open
- confirm Nginx is running
- confirm the domain is not proxied incorrectly by a DNS/CDN provider
- if validation still fails, the catch-all proxy may intercept the ACME challenge; make sure /.well-known/acme-challenge/ is served locally and not proxied to Odoo

9. Test HTTPS and redirect behavior

Safe check

After Certbot, confirm the subdomain works over HTTPS and HTTP no longer serves insecure Odoo sessions.

Show command and interpretation

Command or manual check

curl -I http://odoo.example.com/

curl -I https://odoo.example.com/

How to interpret the result

Expected:
- HTTP should return 301 or 308 and redirect to HTTPS
- HTTPS should return 200, 303, or another normal Odoo response
- the certificate should be valid in the browser

Good examples:
- http://odoo.example.com/ returns 301 or 308 with Location: https://odoo.example.com/
- https://odoo.example.com/ returns 200 OK or a normal Odoo redirect
- the browser shows a valid HTTPS certificate

What would be bad:
- HTTP serves Odoo directly without redirecting to HTTPS
- HTTPS fails with certificate errors
- HTTPS returns 502 or 504, which usually means Nginx cannot reach Odoo locally
- HTTPS returns 403 on the main Odoo homepage, unless you intentionally restricted the whole site
- the browser shows mixed content or wrong generated URLs

Important:
- 403 is bad for the main Odoo homepage
- 403 is good for sensitive /web/database routes if you intentionally block them publicly

If HTTPS works but Odoo shows wrong URLs or mixed content, check:
- proxy_mode = True in odoo.conf
- X-Forwarded-Proto header in Nginx
- X-Forwarded-Host / Host headers in Nginx
- Odoo was restarted after config changes

10. Test database manager protection publicly

Safe check

The Odoo database manager is sensitive. Public access should be blocked or intentionally restricted.

Show command and interpretation

Command or manual check

curl -I https://odoo.example.com/web/database/manager

curl -I https://odoo.example.com/web/database/selector

curl -I https://odoo.example.com/web/database/create

How to interpret the result

Target public result:
- 403 Forbidden
- or another intentionally blocked/restricted result

Avoid public 200 OK on database manager routes unless you fully understand and accept the risk.

Even if these routes are blocked in Nginx:
- keep admin_passwd strong
- keep list_db = False for production-style setups
- keep dbfilter configured

11. Verify Odoo configuration for proxy mode

Caution

Odoo should know it is behind a trusted reverse proxy so HTTPS, cookies, and generated URLs behave correctly.

Show command and interpretation

Command or manual check

# Go to the folder where your Odoo deployment is managed.
# Example for a Docker Compose install:
cd /opt/odoo-secure

# Review the important proxy/security settings without printing the master password.
grep -E "^(proxy_mode|list_db|dbfilter|admin_passwd)" config/odoo.conf \
  | sed -E 's/(admin_passwd) = .+/\1 = ***hidden***/'

# If you changed odoo.conf, restart Odoo.
sudo docker compose restart odoo

# Re-test HTTPS after restart.
curl -I https://odoo.example.com/

# Re-test public database manager protection.
curl -I https://odoo.example.com/web/database/manager

How to interpret the result

Recommended:
- proxy_mode = True
- list_db = False
- dbfilter = ^your_database_name$
- admin_passwd is strong and not printed publicly

The command should be executed from the folder where your Odoo deployment is managed.

Examples:
- /opt/odoo-secure for a dedicated Docker Compose deployment
- another project folder if docker-compose.yml and config/odoo.conf are stored elsewhere
- /etc/odoo/odoo.conf for some package-based Linux installs

If you changed odoo.conf:
- restart the Odoo container or service
- re-test the HTTPS homepage
- re-test that /web/database routes are blocked or restricted publicly

For Docker Compose:
- sudo docker compose restart odoo

For a package-based Linux install, the service may be:
- sudo systemctl restart odoo
- sudo systemctl restart odoo-server

Good HTTPS result:
- https://odoo.example.com/ returns 200 OK, 303, or another normal Odoo response

Good database manager protection result:
- https://odoo.example.com/web/database/manager returns 403, 404, or another intentionally restricted response

12. Final public exposure check

Safe check

Before considering Odoo online, verify only intended public ports are reachable and Odoo/PostgreSQL remain private.

Show command and interpretation

Command or manual check

sudo ss -tulpn | grep -E ":(22|80|443|5432|8069|8072)"

sudo ufw status verbose

docker ps --format "table {{.Names}}\t{{.Ports}}"

How to interpret the result

Good final state:
- 22 is public only if SSH is needed
- 80 and 443 are public for Nginx
- 8069 is bound to 127.0.0.1 only
- 8072 is bound to 127.0.0.1 only
- PostgreSQL is not public
- Docker does not publish PostgreSQL publicly
- firewall does not allow 8069, 8072, or 5432 publicly

Final verification

  • https://odoo.example.com opens Odoo successfully.
  • HTTP redirects to HTTPS.
  • The certificate is valid in the browser.
  • Odoo 8069 is not publicly exposed.
  • Odoo 8072 is not publicly exposed.
  • PostgreSQL 5432 is not publicly exposed.
  • /web/database routes are blocked or intentionally restricted.
  • proxy_mode is enabled in the active Odoo configuration.
  • Nginx test passes with nginx -t.
  • Certbot renewal is installed and can renew certificates.

Need help exposing Odoo safely?

If you want to put Odoo behind Nginx, HTTPS, and a clean subdomain without exposing port 8069, port 8072, PostgreSQL, or database manager routes publicly, I can help review or set up the deployment.

I can also help troubleshoot common deployment issues such as wrong proxy headers, broken websocket routing, Certbot failures, HTTP not redirecting to HTTPS, or Odoo generating incorrect URLs.

Contact me for Odoo HTTPS setup →

Related guide

Before hosting Odoo publicly, first make sure the underlying VPS is hardened and audited.

How to secure Odoo on a VPS →