Most "Odoo on VPS" tutorials get you to a login page. This one gets you to a server that still works after a reboot, a traffic spike, and a bad deploy — because that's where the real failures live.
I run Odoo 17 in production for a retail business (e-commerce with payments, POS, an AI chatbot, live since October 2026). Here's the exact stack, and the exact mistakes I keep seeing.
1. PostgreSQL: don't let Odoo own it
Odoo's installer wants to run everything as one user. Resist.
- Create a dedicated
postgres-managed OS user for Odoo (odoo), and let PostgreSQL handle authentication via its own roles and the local socket — no TCP on localhost, no password inodoo.conffor local connections. - Set
shared_buffersto ~25% of RAM for a small dedicated box (1 GB on a 4 GB VPS is a sane start) and leave the rest to the OS cache. - Keep the database on the same machine as Odoo unless you know you need otherwise. Network latency between Odoo and Postgres is a debugging session you don't want.
2. Nginx + WebSocket: the chat killer
Odoo's realtime (chat, live updates) uses a WebSocket on /websocket. If your Nginx config only proxies HTTP, everything "works" until someone opens the chat, then it silently degrades.
location /websocket { proxy_pass http://127.0.0.1:8069; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 86400; } location / { proxy_pass http://127.0.0.1:8069; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } Also set limit_time_real high enough for your slowest report (PDF generation via wkhtmltopdf is the classic surprise), and give Odoo at least two workers — with one worker, wkhtmltopdf deadlocks waiting on the server it's printing from.
3. systemd: the "works until reboot" fix
Running Odoo with nohup ./odoo-bin ... is how you forget the environment file. Your tokens, mail credentials and database URL live in an env file; if systemd doesn't load it, Odoo starts fine and then fails in confusing ways an hour later.
[Service] User=odoo EnvironmentFile=/etc/odoo/odoo.env ExecStart=/opt/odoo/odoo-bin -c /etc/odoo/odoo.conf Restart=always After every upgrade: systemctl restart odoo — workers cache code, views, and environment variables.
4. TLS that renews itself
Let's Encrypt + certbot with the Nginx plugin, and verify the renewal timer actually runs (systemctl list-timers | grep certbot). An expired cert on a business site is a Monday-morning phone call.
5. Backups you have restored once
A backup you've never restored is a hypothesis. My rule: daily pg_dump + filestore copy, and a documented restore test — to a scratch database, on a schedule you'd actually follow. If the restore takes four hours and three wiki pages, your backup is fiction.
The boring checklist that saves weekends
- [ ] Postgres on socket, dedicated user, tuned
shared_buffers - [ ] Nginx WebSocket block on
/websocket - [ ] ≥2 workers, generous
limit_time_real - [ ] systemd with
EnvironmentFile,Restart=always - [ ] Auto-renewing TLS
- [ ] Restored-backup drill
- [ ] Deploys with a backup first, and
grep ERRORin the log after
That's the whole job. Odoo itself is the easy part — the server around it is where production lives.
I deploy and secure Odoo on VPS servers for a living — Nginx, WebSocket, SSL, systemd, PostgreSQL tuning and restore-tested backups. If your Odoo deserves better than a tutorial: my Fiverr gig.


