How to Install Odoo 17 on Ubuntu 24.04 VPS: Self-Hosted Open-Source ERP
Deploying Odoo on your own VPS gives you a full enterprise resource planning platform — CRM, Sales, Inventory, Accounting, HR, Website, eCommerce, Manufacturing — running on infrastructure you control, with no per-user SaaS fees and no vendor lock-in. This guide walks you through installing Odoo 17 Community Edition on Ubuntu 24.04 from source, with PostgreSQL 16, a hardened systemd service, Nginx reverse proxy with WebSocket support for longpolling, and Let's Encrypt TLS.
Prefer a managed platform? If you want an Odoo-ready VPS with PostgreSQL tuned and Nginx pre-configured, deploy in minutes on our CloudCore Business plan and follow this guide straight through to a production-grade install.
Table of Contents
What is Odoo?
Odoo is a modular open-source business management suite that competes directly with SAP, Oracle NetSuite, and Microsoft Dynamics. Originally released in 2005 as TinyERP, it has grown into a platform of more than 40 official modules plus thousands of community-maintained apps, all built on a single Python/PostgreSQL core with a consistent data model and ORM.
The modular architecture is what makes Odoo stand out. You install only the modules your business needs and they work together natively. CRM captures leads and tracks the sales pipeline; Sales turns quotes into orders that flow directly into Inventory for picking and shipping; stock moves automatically generate journal entries in Accounting; HR manages employees, time off, payroll, and expenses; Website and eCommerce run your public site and online store against the same product catalog; Manufacturing handles MRP, work orders, and bills of materials; Project tracks tasks and timesheets that feed back into invoicing. There is no integration layer to maintain — every module reads and writes the same database tables.
The Community Edition is released under the LGPLv3 license, which means you can self-host it, modify it, and use it commercially without paying Odoo SA a cent. Enterprise Edition adds features like Studio (low-code app builder), Accounting full localizations, and official mobile apps, but Community covers the core functionality that most small and medium businesses actually use day to day. The full Odoo 17 documentation is publicly available and thorough.
Typical deployments range from a solo consultant using CRM and Invoicing, to a 200-person distributor running CRM + Sales + Inventory + Accounting + Purchase + Manufacturing, to a SaaS provider hosting dozens of isolated Odoo databases for their customers. The same stack scales from 1 user to several hundred on a properly sized VPS.
Why Self-Host Odoo on Your VPS?
Running Odoo on your own Ubuntu server instead of paying for Odoo.sh or Odoo Online — or worse, licensing SAP Business One or NetSuite — comes with concrete benefits:
- Zero per-user licensing fees — Odoo Community is free forever. Odoo Online charges roughly EUR 24 per user per month, and SAP Business One or NetSuite routinely run upwards of EUR 100 per user per month plus implementation fees. A 20-person team saves EUR 5,000+ per year versus Odoo Online alone, and tens of thousands versus legacy ERPs.
- Full data ownership — your customer list, invoices, inventory, and financial records stay on a server you control. No data is transmitted to a third-party ERP vendor. This is essential for regulated industries and GDPR-sensitive EU businesses.
- Unlimited custom modules — you can install any OCA (Odoo Community Association) module, write your own, or fork existing ones. Odoo.sh restricts what you can deploy; self-hosting does not.
- No user count caps or artificial limits — add staff without the accountant flinching. Create dozens of databases for sandboxing or multi-company setups without extra fees.
- Database-level access — direct PostgreSQL access lets you run complex SQL reports, build Metabase or Grafana dashboards on live data, or replicate to a warehouse. SaaS Odoo does not expose the database.
- Predictable cost — one VPS monthly invoice covers everything. No surprise overages when you onboard a new team.
- Hardware flexibility — scale vertically by moving to a larger VPS or horizontally by splitting the web and database tiers. You choose the infrastructure.
Cost Comparison: Self-Hosted Odoo vs. SaaS ERP
| Scenario (20 users) | SAP Business One | NetSuite | Odoo Online | Self-Hosted Odoo (VPS) |
|---|---|---|---|---|
| Monthly cost | ~EUR 2,000+ | ~EUR 2,500+ | ~EUR 480 | EUR 29.99/mo (flat) |
| Per-user fee | Yes | Yes | Yes (EUR 24/user) | No |
| Implementation | EUR 20k+ | EUR 30k+ | Low | DIY or partner |
| Custom modules | Expensive addons | Limited | Restricted | Unlimited |
| Data ownership | Vendor-hosted | Vendor-hosted | Vendor-hosted | Yours |
| Typical 3-year TCO | EUR 90k+ | EUR 120k+ | ~EUR 17k | ~EUR 1.1k |
Prerequisites
Before you start, make sure you have:
- A VPS running Ubuntu 24.04 LTS with root or sudo access
- SSH access to the server
- At least 4 GB of RAM (8 GB recommended for 10+ concurrent users)
- At least 40 GB of disk space (filestore and database grow with attachments)
- A domain name pointed at the VPS IP (for TLS — optional, but strongly recommended)
- Ports 80 and 443 open in your firewall for Nginx and Let's Encrypt
Recommended Plan: CloudCore Business>
For a production Odoo install serving a small team, we recommend the CloudCore Business plan:>
- 6 vCPU cores
- 16 GB RAM
- 200 GB NVMe SSD
- Unmetered bandwidth>
This comfortably handles Odoo + PostgreSQL + Nginx on one box with room for 20–40 concurrent users and 2–4 worker processes. For larger teams or multi-tenant deployments, scale up vCPU and RAM before splitting tiers.
Connect to your server:
ssh root@your-server-ipStep 1: Update System Packages and Install Dependencies
Start by refreshing the package index and installing the build tools and libraries Odoo needs to compile its Python dependencies.
sudo apt update && sudo apt upgrade -yInstall the system packages:
sudo apt install -y git python3 python3-pip python3-dev python3-venv \
build-essential wget curl nodejs npm \
libxml2-dev libxslt1-dev libldap2-dev libsasl2-dev libtiff5-dev \
libjpeg8-dev libopenjp2-7-dev zlib1g-dev libfreetype6-dev liblcms2-dev \
libwebp-dev libharfbuzz-dev libfribidi-dev libxcb1-dev libpq-dev \
libffi-dev libssl-devInstall the rtlcss npm package, which Odoo uses to generate right-to-left CSS (required for Arabic, Hebrew, and similar locales):
sudo npm install -g rtlcssStep 2: Install PostgreSQL 16 and Create the Odoo Role
Odoo 17 officially supports PostgreSQL 12 through 16. We'll install PostgreSQL 16 from Ubuntu's default repositories.
sudo apt install -y postgresql postgresql-contribCheck that the service is running:
sudo systemctl status postgresqlExpected output:
● postgresql.service - PostgreSQL RDBMS
Loaded: loaded (/lib/systemd/system/postgresql.service; enabled; preset: enabled)
Active: active (exited) since Wed 2026-04-16 10:00:00 UTC; 5s agoCreate a PostgreSQL role that matches the Linux system user we'll create in Step 4. Odoo connects to PostgreSQL using peer authentication when the DB and app are on the same host, so the PostgreSQL role name must equal the OS user name.
sudo -u postgres createuser --createdb --username postgres --no-createrole --no-superuser --pwprompt odoo17You will be prompted to set a password for the odoo17 role. Choose a strong password and keep it handy — you'll put it in odoo.conf in Step 7.
The --createdb flag lets the odoo17 role create new databases, which is how Odoo's database manager creates fresh tenants. The role intentionally is not a superuser — it only has the permissions it needs.
For a deeper look at PostgreSQL tuning, see our PostgreSQL install guide.
Step 3: Install wkhtmltopdf 0.12.6 (Patched Qt)
Odoo uses wkhtmltopdf to render PDF reports (invoices, quotes, delivery slips). It must be version 0.12.6 or later built against a patched Qt — the regular Ubuntu package is built against unpatched Qt and will produce broken headers and footers.
Download and install the official .deb package:
cd /tmp
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.jammy_amd64.deb
sudo apt install -y ./wkhtmltox_0.12.6.1-3.jammy_amd64.debThejammy(22.04) build also works correctly on Ubuntu 24.04 (noble). The wkhtmltopdf project did not publish a separate noble build as of this writing.
Verify the version:
wkhtmltopdf --versionExpected output:
wkhtmltopdf 0.12.6.1 (with patched qt)The (with patched qt) suffix is the important part — if you see anything else, Odoo reports will have layout issues.
Step 4: Create the Odoo System User and Directory Layout
Create a dedicated non-login system user to run the Odoo service. Running Odoo as root is a security mistake.
sudo useradd -m -d /opt/odoo17 -U -r -s /bin/bash odoo17Flag breakdown:
-m -d /opt/odoo17— create the home directory at/opt/odoo17-U— create a group with the same name-r— system account (UID below 1000, no expiration)-s /bin/bash— shell is needed because we'llsuinto this account to run git and pip
sudo mkdir -p /var/log/odoo17
sudo chown odoo17:odoo17 /var/log/odoo17Create the custom addons directory (we'll use this in the Post-Install section):
sudo mkdir -p /opt/odoo17/custom-addons
sudo chown odoo17:odoo17 /opt/odoo17/custom-addonsStep 5: Clone Odoo 17 from GitHub
Switch to the odoo17 user and clone the Odoo 17 branch. A shallow clone (--depth 1) saves several gigabytes and cuts the download time dramatically.
sudo su - odoo17git clone --depth 1 --branch 17.0 https://github.com/odoo/odoo.git /opt/odoo17/odooExpected output:
Cloning into '/opt/odoo17/odoo'...
remote: Enumerating objects: 45120, done.
remote: Counting objects: 100% (45120/45120), done.
remote: Compressing objects: 100% (36521/36521), done.
remote: Total 45120 (delta 12103), reused 21456 (delta 6342), pack-reused 0
Receiving objects: 100% (45120/45120), 312.45 MiB | 48.21 MiB/s, done.
Resolving deltas: 100% (12103/12103), done.
Updating files: 100% (42103/42103), done.The full clone (without --depth 1) is around 4 GB; the shallow clone is closer to 400 MB.
Step 6: Create a Python Virtual Environment and Install Requirements
Still as the odoo17 user, create an isolated Python virtual environment so Odoo's dependencies don't collide with system Python packages.
python3 -m venv /opt/odoo17/venv
source /opt/odoo17/venv/bin/activateUpgrade pip and install Odoo's requirements:
pip install --upgrade pip wheel
pip install -r /opt/odoo17/odoo/requirements.txtThis step compiles several native extensions (psycopg2, lxml, Pillow, python-ldap) and typically takes 3–8 minutes depending on CPU. If a package fails to compile, the most common cause is a missing system library — re-check Step 1.
When the install finishes, exit the odoo17 shell back to your sudo user:
exitStep 7: Create the odoo.conf Configuration File
Create the configuration file that Odoo reads at startup:
sudo tee /etc/odoo17.conf > /dev/null <<'EOF' [options] ; Admin master password — required to create, restore, or drop databases ; Generate a strong one: openssl rand -base64 32 admin_passwd = CHANGE_THIS_TO_A_LONG_RANDOM_STRING; PostgreSQL connection db_host = False db_port = False db_user = odoo17 db_password = CHANGE_THIS_TO_THE_DB_PASSWORD_FROM_STEP_2
; Addons path — built-in Odoo modules + our custom folder addons_path = /opt/odoo17/odoo/addons,/opt/odoo17/custom-addons
; Data & log locations data_dir = /opt/odoo17/data logfile = /var/log/odoo17/odoo17.log log_level = info
; Networking xmlrpc_port = 8069 longpolling_port = 8072 proxy_mode = True
; Workers — multiprocessing mode for production ; Formula: workers = (2 * CPU_cores) + 1, capped by RAM workers = 3
; Per-worker memory limits (bytes) — prevents runaway requests from OOMing the box limit_memory_soft = 2147483648 limit_memory_hard = 2684354560
; Per-request CPU/wall time limits (seconds) limit_time_cpu = 600 limit_time_real = 1200
; Database listing — disable in production to prevent DB enumeration from the login page list_db = False EOF
Secure the file — it contains two secrets:
sudo chown odoo17:odoo17 /etc/odoo17.conf
sudo chmod 640 /etc/odoo17.confCreate the data directory referenced in the config:
sudo mkdir -p /opt/odoo17/data
sudo chown odoo17:odoo17 /opt/odoo17/dataKey settings explained:
admin_passwd— master password that protects the/web/database/managerendpoint. Anyone with this password can create, drop, or restore any database. Treat it like a root password.proxy_mode = True— tells Odoo to honorX-Forwarded-ForandX-Forwarded-Protoheaders from Nginx so it knows the real client IP and scheme. Required when running behind a reverse proxy.workers = 3— starts Odoo in multiprocessing mode with 3 HTTP workers plus one cron worker. On a 6 vCPU / 16 GB RAM box, 3–4 workers is the sweet spot. Settingworkers = 0runs Odoo in threaded mode (dev only — not suitable for production).longpolling_port = 8072— a second HTTP port that handles long-poll/WebSocket connections for the chatter, live notifications, and Discuss. Nginx must proxy this port separately.limit_memory_hard— the hard cap; a worker exceeding this is killed and respawned by the master. Set to ~2.5 GB per worker on a 16 GB server to leave headroom for PostgreSQL and the OS.list_db = False— hides the database selector on the login page. Combined withdbfilter, this means end users never see other tenants' database names.
Step 8: Create the systemd Unit
Create a systemd service so Odoo starts on boot and restarts on failure.
sudo tee /etc/systemd/system/odoo17.service > /dev/null <<'EOF' [Unit] Description=Odoo 17 Community Documentation=https://www.odoo.com/documentation/17.0/ Requires=postgresql.service After=network.target postgresql.service[Service] Type=simple User=odoo17 Group=odoo17 ExecStart=/opt/odoo17/venv/bin/python3 /opt/odoo17/odoo/odoo-bin -c /etc/odoo17.conf StandardOutput=journal+console Restart=on-failure RestartSec=5s
; Hardening NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=read-only ReadWritePaths=/opt/odoo17 /var/log/odoo17
[Install] WantedBy=multi-user.target EOF
Reload systemd, enable the service so it survives reboots, and start it:
sudo systemctl daemon-reload
sudo systemctl enable odoo17
sudo systemctl start odoo17Check status:
sudo systemctl status odoo17Expected output (abbreviated):
● odoo17.service - Odoo 17 Community
Loaded: loaded (/etc/systemd/system/odoo17.service; enabled; preset: enabled)
Active: active (running) since Wed 2026-04-16 10:30:00 UTC; 5s ago
Main PID: 12345 (python3)
Tasks: 5 (limit: 19128)
Memory: 185.0M
CPU: 2.134s
CGroup: /system.slice/odoo17.service
├─12345 /opt/odoo17/venv/bin/python3 /opt/odoo17/odoo/odoo-bin -c /etc/odoo17.conf
├─12346 odoo: worker cron 0
├─12347 odoo: worker 0
├─12348 odoo: worker 1
└─12349 odoo: worker 2Tail the log to confirm there are no errors:
sudo tail -f /var/log/odoo17/odoo17.logYou should see HTTP service (werkzeug) running on 0.0.0.0:8069 followed by worker startup messages.
Step 9: Configure Nginx as a Reverse Proxy with WebSocket Support
Odoo listens on ports 8069 (HTTP) and 8072 (longpolling). You never expose these directly — Nginx sits in front, terminates TLS, and proxies to both ports.
Install Nginx:
sudo apt install -y nginxOur Nginx install guide has a full walkthrough of hardening and tuning if you want to go deeper.
Create the Odoo site config:
sudo tee /etc/nginx/sites-available/odoo17 > /dev/null <<'EOF'Upstreams — Odoo multi-process
upstream odoo_backend { server 127.0.0.1:8069; } upstream odoo_longpoll { server 127.0.0.1:8072; }Redirect HTTP to HTTPS (Certbot will replace this after TLS is installed)
server { listen 80; server_name odoo.yourdomain.com; return 301 https://$host$request_uri; }server { listen 443 ssl http2; server_name odoo.yourdomain.com;
# TLS certs (filled in by Certbot in Step 10) ssl_certificate /etc/letsencrypt/live/odoo.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/odoo.yourdomain.com/privkey.pem;
# Security headers add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options SAMEORIGIN always; add_header Referrer-Policy strict-origin-when-cross-origin always;
# Large uploads (product images, attachments) client_max_body_size 200m;
# Proxy timeouts — some Odoo operations (imports, installs) are slow proxy_read_timeout 720s; proxy_connect_timeout 720s; proxy_send_timeout 720s;
# Forward real client info 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_redirect off;
# Gzip compression gzip on; gzip_min_length 1000; gzip_types text/xml text/plain text/css application/xml application/xhtml+xml application/rss+xml application/atom+xml application/json application/javascript application/x-javascript text/javascript image/svg+xml;
# Longpolling / WebSocket — chatter, live notifications, Discuss location /websocket { proxy_pass http://odoo_longpoll; proxy_http_version 1.1; 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; } location /longpolling { proxy_pass http://odoo_longpoll; }
# Cache static assets aggressively location ~* /web/static/ { proxy_cache_valid 200 90m; proxy_buffering on; expires 864000; proxy_pass http://odoo_backend; }
# Everything else → Odoo HTTP workers location / { proxy_pass http://odoo_backend; } } EOF
Enable the site, remove the default, and test the config:
sudo ln -s /etc/nginx/sites-available/odoo17 /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -tDon't reload Nginx yet — the TLS certs referenced in the config don't exist. Certbot will handle that in the next step.
Step 10: Obtain a Let's Encrypt TLS Certificate
Install Certbot and the Nginx plugin:
sudo apt install -y certbot python3-certbot-nginxTemporarily comment out the two ssl_certificate* lines in /etc/nginx/sites-available/odoo17 and the listen 443 ssl http2; line (or change it to listen 80; on a different server_name), reload Nginx, then run Certbot. Alternatively, use the standalone mode by stopping Nginx briefly:
sudo systemctl stop nginx
sudo certbot certonly --standalone -d odoo.yourdomain.com \
--non-interactive --agree-tos -m [email protected]
sudo systemctl start nginx
sudo nginx -t && sudo systemctl reload nginxExpected Certbot output:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/odoo.yourdomain.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/odoo.yourdomain.com/privkey.pem
This certificate expires on 2026-07-15.Certbot installs a systemd timer that auto-renews certificates before expiry — verify:
sudo systemctl list-timers | grep certbotSee our dedicated Let's Encrypt with Certbot guide for alternative ACME clients (Caddy, acme.sh) and wildcard DNS-01 setups.
Visit https://odoo.yourdomain.com in your browser — you should now see the Odoo database manager page.
Step 11: Create Your First Database and Install Apps
Navigate to https://odoo.yourdomain.com/web/database/manager. You will see a form asking for:
- Master Password — the
admin_passwdvalue from/etc/odoo17.conf - Database Name — e.g.
acme_prod(lowercase, underscores, no spaces) - Email — the first admin user's login
- Password — the first admin user's password
- Language / Country — used for default localization (chart of accounts, tax rules, date formats)
- Demo data — uncheck for production
Install Core Apps
From the Apps menu, remove the Apps filter chip (which hides technical modules by default) and install the apps you need. Good starting points:
- CRM — lead and opportunity management
- Sales — quotations, sales orders, customer portal
- Invoicing — if you only need billing without the full Accounting module
- Inventory — stock, warehouses, transfers
- Purchase — supplier orders and bills
- Accounting — full GL, reconciliation, localized chart of accounts (Community version has limitations vs Enterprise)
- Website + eCommerce — public-facing site and online store
- Employees + Time Off + Expenses — HR basics
Once your database works end to end, lock down database operations — go to the database manager URL, note that the list_db = False setting already hides databases on the login screen, and make sure your admin_passwd is a long random string (not the default).
Post-Install: Custom Addons and OCA Modules
The real power of self-hosted Odoo is the module ecosystem. The Odoo Community Association (OCA) maintains hundreds of high-quality free modules on GitHub.
Install an OCA Repository
Switch to the odoo17 user and clone the repo into the custom addons folder. Example — account-financial-reports, which adds General Ledger, Aged Partner Balance, Trial Balance, and other reports that Community edition lacks:
sudo su - odoo17
cd /opt/odoo17/custom-addons
git clone --depth 1 --branch 17.0 https://github.com/OCA/account-financial-reports.gitInstall the OCA repo's Python dependencies (most have a requirements.txt):
source /opt/odoo17/venv/bin/activate
pip install -r /opt/odoo17/custom-addons/account-financial-reports/requirements.txt 2>/dev/null || true
exitRestart Odoo and update the app list from Apps → Update Apps List:
sudo systemctl restart odoo17The new modules now appear in the Apps screen and install like any built-in module.
Writing Your Own Module
Create a new module skeleton inside /opt/odoo17/custom-addons/my_module/ with an __init__.py, __manifest__.py, and the usual models/, views/, security/ folders. After saving, restart Odoo and Update Apps List. The Odoo developer tutorial walks through a full custom module step by step.
Multi-Database Setup and Database Filtering
Odoo can host multiple isolated databases on the same server — useful for dev/staging/prod separation, multi-company setups with distinct legal entities, or SaaS-style customer hosting.
Create additional databases via the /web/database/manager URL. To route different domains to different databases, use the dbfilter option in odoo.conf:
; Match database name to subdomain — e.g. acme.yourdomain.com → acme database
dbfilter = ^%d$
list_db = FalseThe %d placeholder expands to the first component of the Host header. A request to acme.yourdomain.com only loads the acme database; globex.yourdomain.com only loads globex. Combined with list_db = False, users can never see or access any database except the one mapped to their hostname.
Point multiple subdomains at the same VPS (A records in DNS) and add them as server_name entries in the Nginx config. Odoo handles the rest.
Daily Backups (PostgreSQL + Filestore)
Odoo data lives in two places:
/opt/odoo17/data/filestore/<db_name>/You must back up both for a restore to work. Backing up only the database leaves you with dangling attachment references.
Create a backup script:
sudo tee /usr/local/bin/odoo-backup.sh > /dev/null <<'EOF'
#!/bin/bash
set -euo pipefailBACKUP_DIR="/var/backups/odoo17"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
RETENTION_DAYS=14
DATABASES=$(sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datistemplate = false AND datname NOT IN ('postgres');")
mkdir -p "$BACKUP_DIR"
for DB in $DATABASES; do
echo "Backing up $DB..."
# 1. PostgreSQL dump
sudo -u postgres pg_dump -Fc "$DB" > "$BACKUP_DIR/${DB}-${TIMESTAMP}.dump"
# 2. Filestore tarball
if [ -d "/opt/odoo17/data/filestore/$DB" ]; then
tar -czf "$BACKUP_DIR/${DB}-filestore-${TIMESTAMP}.tar.gz" \
-C /opt/odoo17/data/filestore "$DB"
fi
done
Prune old backups
find "$BACKUP_DIR" -type f -mtime +$RETENTION_DAYS -delete
echo "Backup complete — $BACKUP_DIR"
EOF
sudo chmod +x /usr/local/bin/odoo-backup.shSchedule it via cron to run nightly at 02:00:
echo "0 2 * root /usr/local/bin/odoo-backup.sh >> /var/log/odoo17/backup.log 2>&1" | sudo tee /etc/cron.d/odoo-backupTest a restore before you need one. Create a throwaway database via the manager, restore the most recent dump and filestore into it, and log in to verify. An untested backup is not a backup.
For offsite storage, pipe the backup files to S3-compatible storage (Contabo Object Storage, Backblaze B2, AWS S3) with rclone or aws s3 sync at the end of the script.
Updating Odoo
Odoo 17 receives regular bugfix and security updates on the 17.0 branch. Update monthly:
sudo systemctl stop odoo17
sudo su - odoo17
cd /opt/odoo17/odoo
git pull origin 17.0
source /opt/odoo17/venv/bin/activate
pip install -r requirements.txt --upgrade
exit
sudo systemctl start odoo17After a pull that touches module code, log into each database as admin and run Apps → Update Apps List → then upgrade any modules flagged with an available update. For safety, restart Odoo with -u all once to run all module updates:
sudo systemctl stop odoo17
sudo -u odoo17 /opt/odoo17/venv/bin/python3 /opt/odoo17/odoo/odoo-bin \
-c /etc/odoo17.conf -d your_db_name -u all --stop-after-init
sudo systemctl start odoo17Never jump major versions in place. Going from Odoo 16 to 17 requires a proper migration (OpenUpgrade or a paid Odoo SA migration) — don't just change the branch.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
| PDF reports have broken headers/footers or wrong layout | wkhtmltopdf not built against patched Qt | Verify wkhtmltopdf --version shows (with patched qt). Reinstall from the GitHub release in Step 3, not from apt install wkhtmltopdf. |
Worker (pid:12345) memory limit (2684354560) reached in logs, HTTP 500s | Single request blew past limit_memory_hard | Increase limit_memory_hard in odoo.conf (2 GB → 3 GB) and restart. Investigate the offending request — likely an import or a buggy report. |
| Login succeeds, page redirects back to login (login loop) | proxy_mode = True missing, or Nginx not forwarding X-Forwarded-Proto | Verify proxy_mode = True in /etc/odoo17.conf. Verify Nginx has proxy_set_header X-Forwarded-Proto $scheme;. Clear browser cookies for the domain. |
| Chatter, notifications, or Discuss don't update live | /websocket or /longpolling not proxied to port 8072 | Check Nginx config has both location blocks from Step 9 and that longpolling_port = 8072 is set in odoo.conf. curl -I https://odoo.yourdomain.com/longpolling/poll should not 404. |
psycopg2.OperationalError: FATAL: Peer authentication failed for user "odoo17" | PostgreSQL role name doesn't match OS user, or password is wrong | Confirm the PostgreSQL role created in Step 2 is named exactly odoo17. Reset its password: sudo -u postgres psql -c "ALTER USER odoo17 WITH PASSWORD 'newpass';" and update db_password in /etc/odoo17.conf. |
| Odoo starts but uses only one core regardless of load | workers = 0 (threaded mode) | Set workers = 3 (or 2*CPU+1) in /etc/odoo17.conf and restart. Threaded mode is dev-only. |
Internal Server Error after git pull update | DB schema out of sync with code | Run odoo-bin -u all --stop-after-init once for each database as shown in the Updating section, then restart the service. |
Can't access /web/database/manager — 403 or blank page | list_db = False blocks listing, but manager URL should still work with master password | Master password prompt appears on the page. If blank, check Odoo logs for werkzeug errors and ensure Nginx isn't caching a 403. |
Installer fails with ERROR: Failed building wheel for psycopg2 | Missing libpq-dev or python3-dev | sudo apt install libpq-dev python3-dev build-essential and re-run pip install -r requirements.txt. |
| Web UI is slow, high CPU on one worker | Single request monopolizing worker — often a report with no indexes | Check /var/log/odoo17/odoo17.log for long-running queries. Add PostgreSQL indexes on filtered/sorted columns. Increase limit_time_real. |
Viewing Logs
Stream the Odoo log live:
sudo tail -f /var/log/odoo17/odoo17.logOr view via journalctl:
sudo journalctl -u odoo17 -fPostgreSQL slow-query log (useful for performance debugging) lives at /var/log/postgresql/postgresql-16-main.log.
FAQ
What's the difference between Odoo Community and Odoo Enterprise?
Community (what this guide installs) is free LGPLv3 open source. It includes all the core modules — CRM, Sales, Inventory, Accounting, Purchase, Manufacturing, HR, Website, eCommerce, Project — with fully functional versions. Enterprise adds proprietary modules like Studio (low-code builder), Documents, Sign, Marketing Automation, Full Accounting (bank sync, follow-ups, advanced reports), and official mobile apps, plus SLA-backed support from Odoo SA. Licensing runs approximately EUR 25–50 per user per month depending on the apps. For most SMBs, Community plus OCA modules covers 90%+ of real-world needs; Enterprise is worth it if you specifically want Studio, official accounting localization, or Odoo's direct support.
Can I run Odoo on the same server as other services?
Yes, but plan resource allocation carefully. On a 16 GB VPS you can comfortably run Odoo (3 workers = ~6 GB) + PostgreSQL (2–4 GB shared buffers) + Nginx alongside light services like Nextcloud or a WordPress site. Avoid running Odoo on the same box as memory-hungry apps like ElasticSearch, GitLab, or another ERP. If Odoo is business-critical, give it a dedicated server — the operational simplicity is worth the extra EUR 10–20/month.
How many users can one VPS handle?
As a rough guide on CloudCore Business (6 vCPU / 16 GB RAM) with 3 workers: 30–50 concurrent active users in CRM/Sales/Inventory-heavy workloads, or 100+ casual users (mostly reading dashboards, occasional data entry). The real bottlenecks are PostgreSQL query performance (fix with indexes) and worker count. Scale vertically first (more RAM, more workers, faster CPU) before splitting tiers. Above 100 concurrent users, consider dedicated PostgreSQL on a separate server and a second Odoo app server behind a load balancer.
Is Odoo Community production-ready, or is it a "limited demo" of Enterprise?
Odoo Community is production-ready and used by thousands of real businesses. It is not a crippled trial. You will hit limits if you need Enterprise-only modules (Studio, full Accounting localizations, Marketing Automation) or require Odoo SA's paid support, but the core ERP functionality is fully present. Many businesses run Community forever; others start on Community and only upgrade to Enterprise when a specific Enterprise module becomes essential. OCA modules close most of the gap for free.
How do I migrate from QuickBooks, Xero, SAP, or another ERP?
Odoo has a CSV/XLS import interface on most models (Customers, Products, Bills, Invoices, Journal Entries, etc.) at the top-right ⚙️ → Import records action. Export your existing data, map column headers to Odoo field names, and run a dry-run import first. For complex migrations (with historical data, foreign keys, multi-company) use the Odoo External API (XML-RPC or JSON-RPC) with a Python script — full docs at odoo.com/documentation/17.0/developer/reference/external_api.html. Plan 2–4 weeks of data work for a typical SMB migration; get a sandbox database running first and test end to end before go-live.
Can Odoo be integrated with Stripe, WooCommerce, Shopify, or other external systems?
Yes. Odoo ships with built-in payment acquirers for Stripe, PayPal, Adyen, Authorize.net, Mollie, and more — configure them under Invoicing → Configuration → Payment Providers. eCommerce integrations for Shopify, WooCommerce, Amazon, and eBay are available as OCA modules or paid Enterprise connectors. For anything not covered, Odoo's External API makes it straightforward to build custom integrations with Zapier, Make, n8n (install guide), or direct webhooks.
Next Steps
Now that Odoo is running on your VPS, here are recommended next steps:
- Set up automated off-site backups — extend
/usr/local/bin/odoo-backup.shto push nightly dumps to Contabo Object Storage, Backblaze B2, or AWS S3 withrclone. Test a full restore at least once per quarter.
- Install OCA accounting extensions — Community accounting is functional but missing reports like General Ledger and Trial Balance. Install
account-financial-reportsandaccount-closingfrom OCA to close the gap without buying Enterprise.
- Add monitoring with Prometheus + Grafana — follow our Prometheus and Grafana guides to track Odoo response times, worker memory, and PostgreSQL query rates. Alert on 5xx spikes and DB slow queries.
- Harden SSH and enable Fail2Ban — your Odoo server is now a business-critical asset. Disable root SSH, enforce key-only auth, and install Fail2Ban to block brute-force attempts on both SSH and the Odoo login page.
- Add staging alongside production — clone your prod database into a
stagingdatabase on the same server (or a smaller separate VPS) and point a subdomain at it. Test module upgrades and OCA installs on staging before touching production.
- Connect Odoo to n8n or Make for workflow automation — trigger Slack alerts on new leads, sync contacts to Mailchimp, post invoices to Slack finance channels. Odoo's External API makes it straightforward.
Need more horsepower for Odoo?>
Our CloudCore Business plan is sized for production Odoo deployments — 6 vCPU, 16 GB RAM, 200 GB NVMe SSD, unmetered bandwidth — and comes with a clean Ubuntu 24.04 image ready for this guide.>
- Handles 30–50 concurrent Odoo users comfortably
- NVMe SSD keeps PostgreSQL snappy on large databases
- Unmetered bandwidth for product images, attachments, and customer portal traffic
- Upgrade vCPU/RAM live as your team grows>
Deploy Your Odoo VPS Now — production-grade infrastructure, no per-user ERP fees, ever.