This detailed guide will walk you through the complete process of deploying Odoo 19 on Ubuntu Server 24.04 LTS with Nginx and PostgreSQL 16, using [DeployHQ](https://www.deployhq.com) for automated, zero-downtime deployments.

> **Why this version stack?** Ubuntu 20.04 reached the end of standard support in May 2025, and PostgreSQL 12 went EOL in November 2024 — neither receives security patches without an Ubuntu Pro subscription. Ubuntu 24.04 LTS ships Python 3.12 and PostgreSQL 16 by default, both inside Odoo 19's supported range (Python 3.10-3.14, PostgreSQL ≥13). If you are still on Odoo 18 or Ubuntu 22.04, the steps below work with minor path adjustments (use `/etc/postgresql/14/main/` for the 22.04 default).

## 1. Prerequisites

Before starting the deployment, make sure you have:

- Ubuntu Server 24.04 LTS on the VPS of your choice
- Root or sudo access (for installing dependencies and managing services)
- A domain name pointed to your server's IP
- Minimum 4 GB RAM and 2 CPU cores
- 20 GB storage (more if you plan to host attachments and filestore backups locally)
- A [DeployHQ](https://www.deployhq.com) account for [automated Git-based deployments](https://www.deployhq.com/features/automatic-deployments)

## 2. Initial Server Setup

First, update your system and install required dependencies:

```
# Update system packages
sudo apt update && sudo apt upgrade -y

# Install Odoo's build and runtime dependencies
sudo apt install -y git python3-pip python3-dev python3-venv \
    build-essential wget python3-setuptools nodejs npm \
    python3-wheel libxslt-dev libzip-dev libldap2-dev libsasl2-dev \
    node-less libjpeg-dev zlib1g-dev libpq-dev \
    libxslt1-dev libxml2-dev python3-lxml
```

Ubuntu 24.04 ships Python 3.12 and Node.js 20, both of which work with Odoo 19. Always cross-check the [official Odoo 19 source-install documentation](https://www.odoo.com/documentation/19.0/administration/on_premise/source.html) for the latest dependency list in case Odoo adjusts the package set between minor releases.

## 3. PostgreSQL Installation and Configuration

Install and configure PostgreSQL for Odoo. Ubuntu 24.04's default `postgresql` package installs PostgreSQL 16, which is Odoo 19's recommended major version:

```
# Install PostgreSQL 16 (default on Ubuntu 24.04)
sudo apt install postgresql postgresql-contrib -y

# Create a database role for Odoo
sudo -u postgres createuser -s odoo
sudo -u postgres psql
ALTER USER odoo WITH PASSWORD 'your_secure_password';
\q

# Create the production database
sudo -u postgres createdb odoo_prod --owner=odoo

# Tune PostgreSQL for Odoo workloads
sudo nano /etc/postgresql/16/main/postgresql.conf
```

Add or modify these lines in `postgresql.conf`:

```
listen_addresses = '*'
max_connections = 100
shared_buffers = 256MB
```

Configure client authentication:

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

Add these lines for local and loopback connections:

```
local all all md5
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
```

Restart PostgreSQL:

```
sudo systemctl restart postgresql
```

> **Looking after the database long-term?** Schema migrations are where most Odoo deployments break. Our guide to [safely orchestrating database deployments](https://www.deployhq.com/blog/database-deployments-made-easy-with-deployhq) covers backup-before-migrate hooks, transactional rollback, and how to wire schema changes into a Git workflow.

## 4. Install and Configure Nginx

```
# Install Nginx
sudo apt install nginx -y

# Create the Nginx site configuration
sudo nano /etc/nginx/sites-available/odoo
```

Add the following configuration:

```
upstream odoo {
    server 127.0.0.1:8069;
}

upstream odoochat {
    server 127.0.0.1:8072;
}

server {
    listen 80;
    server_name your_domain.com;

    # Proxy timeouts (long Odoo requests are common)
    proxy_read_timeout 720s;
    proxy_connect_timeout 720s;
    proxy_send_timeout 720s;

    # Forward headers so Odoo sees the real client
    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;

    # Log files
    access_log /var/log/nginx/odoo.access.log;
    error_log /var/log/nginx/odoo.error.log;

    # SSL parameters (Certbot will manage these from Step 8 onwards)
    ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
    ssl_session_timeout 30m;
    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:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # Application traffic
    location / {
        proxy_pass http://odoo;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
    }

    location /longpolling {
        proxy_pass http://odoochat;
    }

    # Cache static assets
    location ~* /web/static/ {
        proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
        proxy_buffering on;
        proxy_cache odoo_cache;
        proxy_cache_valid 200 60m;
        proxy_cache_valid 404 10m;
        proxy_cache_key $request_uri;
        proxy_cache_lock on;
        proxy_cache_lock_timeout 5s;
        proxy_cache_methods GET HEAD;
        proxy_cache_revalidate on;
        proxy_cache_min_uses 1;
        expires 24h;
        add_header Cache-Control "public, no-transform";
    }
}
```

Enable the configuration:

```
sudo ln -s /etc/nginx/sites-available/odoo /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl restart nginx
```

## 5. Install Odoo

```
# Create the Odoo system user
sudo useradd -m -d /opt/odoo -U -r -s /bin/bash odoo

# Clone Odoo 19 from GitHub (swap 19.0 for 18.0 if you need to stay on the previous LTS)
sudo git clone --depth 1 --branch 19.0 https://github.com/odoo/odoo /opt/odoo/odoo-server

# Install Python dependencies
cd /opt/odoo/odoo-server
sudo pip3 install -r requirements.txt

# Create the custom addons directory
sudo mkdir /opt/odoo/custom-addons
sudo chown odoo:odoo /opt/odoo/custom-addons
```

If `pip3 install` complains about a managed Python environment on Ubuntu 24.04 (PEP 668), use a virtualenv: `python3 -m venv /opt/odoo/odoo-server/env && /opt/odoo/odoo-server/env/bin/pip install -r requirements.txt`.

## 6. Configure Odoo

Create the configuration file:

```
sudo mkdir /etc/odoo
sudo nano /etc/odoo/odoo.conf
```

Add the following configuration:

```
[options]
; General Settings
admin_passwd = your_admin_password
db_host = False
db_port = 5432
db_user = odoo
db_password = your_secure_password
addons_path = /opt/odoo/odoo-server/addons,/opt/odoo/custom-addons
default_productivity_apps = True

; HTTP Settings
http_port = 8069
http_enable = True
proxy_mode = True

; Logging Settings
logfile = /var/log/odoo/odoo-server.log
log_level = info

; Performance Tuning
workers = 4
max_cron_threads = 2
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_request = 8192
limit_time_cpu = 600
limit_time_real = 1200

; Security Settings
list_db = False
```

Set proper permissions:

```
sudo chown odoo:odoo /etc/odoo/odoo.conf
sudo chmod 640 /etc/odoo/odoo.conf

# Create log directory
sudo mkdir /var/log/odoo
sudo chown odoo:odoo /var/log/odoo
```

> **Running Odoo for multiple clients?** Agencies and consultancies typically run a separate Odoo instance per client with shared deployment tooling. Our [agency deployment platform](https://www.deployhq.com/for-agencies) gives you per-project access controls and shared SSH keys across hundreds of servers without juggling Jenkins or homegrown scripts.

## 7. Create a Systemd Service

```
sudo nano /etc/systemd/system/odoo.service
```

Add the following content:

```
[Unit]
Description=Odoo
Requires=postgresql.service
After=network.target postgresql.service

[Service]
Type=simple
SyslogIdentifier=odoo
PermissionsStartOnly=true
User=odoo
Group=odoo
ExecStart=/opt/odoo/odoo-server/odoo-bin -c /etc/odoo/odoo.conf
StandardOutput=journal+console
Environment=PATH=/opt/odoo/odoo-server/env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

[Install]
WantedBy=multi-user.target
```

Enable and start the service:

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

## 8. SSL Configuration with Certbot

```
# Install Certbot
sudo apt install certbot python3-certbot-nginx -y

# Obtain an SSL certificate
sudo certbot --nginx -d your_domain.com
```

## 9. Security Hardening

Configure UFW firewall:

```
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
```

Install and configure Fail2ban:

```
sudo apt install fail2ban -y
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local
```

Add an Odoo-specific jail:

```
[odoo]
enabled = true
port = 8069,8072
filter = odoo
logpath = /var/log/odoo/odoo-server.log
maxretry = 5
findtime = 600
bantime = 3600
```

## 10. Backup Configuration

Create the backup script:

```
sudo nano /opt/odoo/backup.sh
```

Add the following:

```
#!/bin/bash
BACKUP_DIR="/var/backups/odoo"
DATABASE="odoo_prod"
BACKUP_DAYS=7

# Create backup directory
mkdir -p ${BACKUP_DIR}

# Backup database
sudo -u postgres pg_dump ${DATABASE} | gzip > "${BACKUP_DIR}/odoo_db_$(date +%Y%m%d_%H%M%S).gz"

# Backup filestore
tar -zcf "${BACKUP_DIR}/odoo_filestore_$(date +%Y%m%d_%H%M%S).tar.gz" /opt/odoo/.local/share/Odoo/filestore/${DATABASE}

# Remove old backups
find ${BACKUP_DIR} -type f -mtime +${BACKUP_DAYS} -delete
```

Make the script executable and schedule a nightly cron:

```
sudo chmod +x /opt/odoo/backup.sh
sudo crontab -e
```

Add a daily backup at 2 AM:

```
0 2 * * * /opt/odoo/backup.sh
```

This is a single-server backup pattern. For production Odoo deployments serving paying customers, follow the **3-2-1 rule** — three copies of your data, on two different media, with one off-site. Pipe the gzipped dumps to S3/Backblaze/your cloud provider of choice, and periodically test a restore on a staging environment before you actually need it.

## 11. Monitoring and Maintenance

Install monitoring tools:

```
sudo apt install htop iotop nethogs -y
```

Configure log rotation:

```
sudo nano /etc/logrotate.d/odoo
```

Add configuration:

```
/var/log/odoo/*.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
    copytruncate
}
```

## 12. Automated Deployments with DeployHQ

### 12.1 Initial DeployHQ Setup

1. Create a [DeployHQ](https://www.deployhq.com) account and a new project.
2. Connect your Git repository (GitHub, GitLab, or Bitbucket).
3. Add your server as a deployment target:

```
# Create the deployment user
sudo useradd -m -s /bin/bash deployhq
sudo usermod -aG odoo deployhq

# Set up SSH key authentication
sudo mkdir -p /home/deployhq/.ssh
sudo nano /home/deployhq/.ssh/authorized_keys

# Paste DeployHQ's public SSH key
sudo chown -R deployhq:deployhq /home/deployhq/.ssh
sudo chmod 700 /home/deployhq/.ssh
sudo chmod 600 /home/deployhq/.ssh/authorized_keys
```

If your Odoo project uses multiple environments — development, staging, and production — connect each server to the same [DeployHQ](https://www.deployhq.com) project so a single Git commit can promote through environments. See [managing dev, staging, and production environments in DeployHQ](https://www.deployhq.com/blog/managing-multiple-environments-with-deployhq-dev-staging-and-production) for the recommended workflow.

### 12.2 Configure Deployment Settings

Create the deployment configuration in DeployHQ:

1. Repository Settings:

```
Branch: main
Repository Type: Git
```

1. Server Configuration:

```
Protocol: SSH
Hostname: your_server_ip
Port: 22
Username: deployhq
Authentication: SSH Key
Deploy Path: /opt/odoo
```

If your VPS is freshly provisioned and you don't have any deployment tooling yet, the [Git-based deployment on a VPS walkthrough](https://www.deployhq.com/blog/setting-up-git-based-deployment-on-a-virtual-private-server-vps) covers the SSH key exchange end-to-end.

### 12.3 Create Deployment Scripts

Create a [deployment script](https://www.deployhq.com/blog/what-is-a-deployment-script) file in your repository:

```
# deploy.sh
#!/bin/bash

# Update custom addons
cd /opt/odoo/custom-addons
git pull origin main

# Install/update Python dependencies
cd /opt/odoo/odoo-server
pip3 install -r requirements.txt

# Update Odoo database (optional — run during scheduled maintenance windows)
python3 odoo-bin -c /etc/odoo/odoo.conf -d odoo_prod -u all

# Set proper permissions
sudo chown -R odoo:odoo /opt/odoo/custom-addons
sudo chmod -R 755 /opt/odoo/custom-addons

# Restart Odoo service
sudo systemctl restart odoo
```

### 12.4 Configure DeployHQ Deployment Commands

Add these commands in DeployHQ's deployment configuration:

```
# Pre-deployment commands
chmod +x deploy.sh

# Deployment commands
./deploy.sh

# Post-deployment commands
curl -X POST https://your_monitoring_service/webhook/deployment
```

For more complex flows — running `pip install` only when `requirements.txt` changes, generating asset bundles, sending Slack notifications conditionally — wire them into a [DeployHQ](https://www.deployhq.com) [build pipeline](https://www.deployhq.com/features/build-pipelines) so the work runs on the [DeployHQ](https://www.deployhq.com) servers and the build artefact is rsync'd into place atomically.

### 12.5 Set Up Deployment Hooks

Create a webhook URL in your repository settings:

```
https://deployhq.com/projects/your-project/deployments/webhook
```

### 12.6 Configure Automatic Deployments

In [DeployHQ](https://www.deployhq.com), set up automatic deployments:

1. Enable automatic deployments in project settings.
2. Configure branch rules:

```
Branch: main
Environment: production
Automatic: true
```

### 12.7 Security Considerations

Limit the `deployhq` user's `sudo` rights to only the commands needed for the deploy script:

```
sudo visudo
```

Add these lines:

```
deployhq ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart odoo
deployhq ALL=(ALL) NOPASSWD: /usr/bin/chown -R odoo\:odoo /opt/odoo/custom-addons
deployhq ALL=(ALL) NOPASSWD: /usr/bin/chmod -R 755 /opt/odoo/custom-addons
```

### 12.8 Deployment Monitoring

Set up deployment notifications:

1. Email notifications:

```
Notification Type: Email
Recipients: your-team@company.com
Events:
  - Deployment Started
  - Deployment Completed
  - Deployment Failed
```

1. Slack integration:

```
Webhook URL: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
Channel: #deployments
Username: DeployHQ Bot
```

### 12.9 Rollback Strategy

Define your **RPO (Recovery Point Objective)** — how much data you can afford to lose — and your **RTO (Recovery Time Objective)** — how long you can be down. For an Odoo production server hosting live transactions, an RPO measured in minutes and an RTO measured in single-digit minutes are typical. That means you need both a fast code rollback path and a recent database backup.

For the code path, a manual rollback script looks like this:

```
# rollback.sh
#!/bin/bash

# Specify the previous working commit
PREVIOUS_COMMIT=$1

# Rollback custom addons
cd /opt/odoo/custom-addons
git reset --hard $PREVIOUS_COMMIT

# Restart Odoo
sudo systemctl restart odoo

# Notify team
curl -X POST https://your_notification_service/webhook/rollback
```

[DeployHQ](https://www.deployhq.com) also ships [one-click rollback](https://www.deployhq.com/features/one-click-rollback) for code — the previous release stays on the server, so reverting is a symlink swap rather than a redeploy. For broader recovery patterns including database state, our guide to [rolling back deployments safely](https://www.deployhq.com/blog/seamless-recovery-how-deployhq-empowers-you-to-rollback-deployments) walks through the trade-offs.

### 12.10 Deployment Verification

Add post-deployment checks:

```
# verify_deployment.sh
#!/bin/bash

# Check Odoo service status
systemctl is-active --quiet odoo
if [$? -ne 0]; then
    echo "Odoo service is not running"
    exit 1
fi

# Check the web application
curl -f -s -S https://your_domain.com/web/health
if [$? -ne 0]; then
    echo "Web application is not responding"
    exit 1
fi

echo "Deployment verification successful"
```

Remember to replace placeholder values (`your_domain.com`, `your-team@company.com`, etc.) with your actual values before running anything.

## Final Steps and Verification

1. Check service status:

```
sudo systemctl status odoo
sudo systemctl status nginx
sudo systemctl status postgresql
```

1. Verify logs:

```
sudo tail -f /var/log/odoo/odoo-server.log
sudo tail -f /var/log/nginx/odoo.error.log
```

1. Test the installation by accessing:

- `https://your_domain.com`

Once Odoo is live, the deployment patterns above let you push code changes without taking the site down — combine them with [zero-downtime deployment workflows](https://www.deployhq.com/features/zero-downtime-deployments) so your users never see a 502 while you ship a bug fix.

Remember to:

- Replace placeholder values (`your_domain.com`, passwords)
- Keep your system updated regularly
- Monitor system resources
- Maintain regular backups and verify restores periodically
- Review logs to catch errors early
- Renew SSL certificates before expiration (Certbot handles this on a timer)

This guide gives you a production-ready Odoo 19 installation on Ubuntu 24.04 with security hardening, automated backups, and a clean [DeployHQ](https://www.deployhq.com) deployment pipeline. Adjust the configuration values based on your server's resources, your team's RPO/RTO targets, and any compliance requirements specific to your industry.

Ready to automate your own Odoo deployments? [Start a free](https://www.deployhq.com/signup)[DeployHQ](https://www.deployhq.com) trial and connect your first repository in a few minutes.

* * *

Questions or stuck on a step? Email us at [support@deployhq.com](mailto:support@deployhq.com) or ping [@deployhq](https://x.com/deployhq) on X — we read every message.

