Skip to content

fatchipschris/odoo-installer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 

Repository files navigation

Odoo CE One-Shot Installer + Server Hardening

A single install_odoo.sh that takes a fresh Ubuntu server to a working, TLS-terminated, reverse-proxied, hardened Odoo Community Edition deployment.

A Davenport Software asset.

The Odoo config and nginx vhost are derived from a working production deployment — the proxy headers, buffer sizes, timeouts, dbfilter/list_db setup, and caching blocks are all real.


What it does (8 steps)

  1. Dependencies — build toolchain, Python 3 + venv libs, image/XML/LDAP dev headers, node-less + rtlcss, PostgreSQL 16 from the official PGDG repo, and the patched wkhtmltopdf 0.12.6.1 (the Qt build Odoo needs for PDF headers/footers — the distro package is not patched).
  2. System user + DB role — a system odoo user, and a PostgreSQL role odoo with LOGIN CREATEDB (not superuser) and a random password.
  3. Odoo source + venv — shallow git clone of the requested branch into /odoo/odoo-server, and a dedicated virtualenv at /odoo/venv built from Odoo's own requirements.txt, keeping Odoo's dependencies isolated from the system Python.
  4. /etc/odoo-server.conf — the Odoo config (settings below), including db_user/db_password for the Postgres role. Owned by odoo, mode 0600.
  5. systemd unit odoo.service — a clean, modern unit with auto-restart and sandboxing.
  6. nginx reverse-proxy vhost — the reverse-proxy template for $DOMAIN, enabled, with the default site removed. The config is validated with nginx -t before any reload, so a bad config never takes the site down — if validation fails the script aborts and leaves the vhost file in place for inspection.
  7. TLScertbot --nginx for a Let's Encrypt cert + automatic HTTP→HTTPS redirect. Non-fatal if DNS isn't pointed yet (prints a re-run command).
  8. Hardeningufw (allows the detected SSH port plus 80/443, default-deny incoming), fail2ban (sshd jail), unattended-upgrades, and sshd hardening (PermitRootLogin no; key-only auth is an opt-in flag).

The run ends with a summary of the URL, service commands, and next steps (DNS + DB creation). Generated secrets (the master password and the Postgres db_password) are written to /root/odoo-install-credentials.txt (mode 0600) rather than printed to the terminal.

Steps are idempotent where reasonable (re-checks for existing user, role, checkout, venv, packages; reuses the credentials already in /etc/odoo-server.conf on a re-run) with clear colour-coded step logging.


Parameters

Set these as environment variables or edit the CONFIG block at the top of the script.

Variable Default Meaning
DOMAIN example.com Public FQDN served by nginx.
DB_NAME odoo Primary database name (must match the subdomain because dbfilter=^%d$).
ODOO_VERSION 18.0 Odoo git branch — validated against NN.N / saas~NN.N; the script aborts on anything else.
ADMIN_EMAIL admin@example.com Email for certbot registration.
ADMIN_PASSWD (empty) Odoo master/admin password. Auto-generated if empty (written to the credentials file, not printed). On a re-run the value already in odoo-server.conf is reused.
ENABLE_GEOFILTER false Add the nginx GeoIP country allowlist (anglosphere + LATAM). Requires a country MMDB at /var/lib/geoip2/dbip-country.mmdb.
ENABLE_CERTBOT true Request a Let's Encrypt cert via certbot --nginx.
SSH_KEY_ONLY false Opt-in. Disable SSH password auth (key-only). Off by default to avoid lockout.
WWW_REDIRECT true Also serve www.$DOMAIN → apex redirect.
FORCE_GIT_RESET false On a re-run, allow a hard git reset of the Odoo checkout (discards local changes). Off by default to protect local patches.

Example

sudo DOMAIN=acme.com DB_NAME=acme ADMIN_EMAIL=ops@acme.com \
     ODOO_VERSION=18.0 ENABLE_GEOFILTER=false \
     ./install_odoo.sh

Settings

/etc/odoo-server.conf

  • proxy_mode = True — trust the X-Forwarded-* headers nginx sets.
  • dbfilter = ^%d$ + list_db = False — subdomain must equal the DB name; the DB manager / selector is hidden. This is how a single host serves multiple isolated databases by subdomain.
  • http_port = 8069, gevent_port = 8072 (longpolling/bus).
  • xmlrpc_interface = 127.0.0.1 — Odoo binds to loopback only; the public edge is nginx.
  • workers = 2, max_cron_threads = 1, and the memory/time limits (limit_memory_soft/hard, limit_time_cpu = 600, limit_time_real = 1200, limit_request = 8192).
  • db_user / db_password for the Postgres role, plus admin_passwd, logfile, addons_path (server addons + custom_addons).

nginx vhost (the reverse proxy)

  • location /http://127.0.0.1:8069; location /longpolling:8072.
  • The X-Forwarded-Host / X-Forwarded-For / X-Forwarded-Proto / X-Real-IP / X-Client-IP header set that proxy_mode = True expects.
  • proxy_buffers 16 64k, proxy_buffer_size 128k, and 900s read/connect/ send timeouts so long reports and module installs don't get cut off.
  • proxy_next_upstream fast-fail on backend errors.
  • gzip block, client_max_body_size 0 (no upload cap), large header buffers.
  • The ~ /[a-zA-Z0-9_-]*/static/ caching block (60m) and the ~* \.(js|css|png|…)$ 2-day cache block.
  • certbot appends the listen 443 ssl / cert paths and the :80:443 redirect.

Design highlights

  • Validated nginx reloads. The vhost is validated with nginx -t before any reload — a broken config never takes the site down. On failure the script aborts and leaves the vhost file in place for inspection.
  • Modern service management. Odoo runs under a clean systemd unit with auto-restart and sandboxing (NoNewPrivileges, ProtectSystem=full, ProtectHome, PrivateTmp, scoped ReadWritePaths).
  • Isolated Python. Odoo's dependencies live in a dedicated virtualenv built from Odoo's own requirements.txt, separate from the system Python.
  • Locked-down multi-tenancy. dbfilter = ^%d$ with list_db = False ties each database to its subdomain and hides the DB manager/selector.
  • Safe SSH changes. Key-only auth is opt-in; the sshd drop-in is validated with sshd -t and removed automatically if validation fails, so you can't lock yourself out. ufw allows the actual sshd port (detected, not hardcoded) before the firewall comes up.
  • Stable credentials. The master password and the Postgres db_password are written into odoo-server.conf (db_user/db_password) and to a 0600 credentials file. A re-run reuses those values instead of rotating them, so it never severs Odoo's connection to Postgres.

Hardening included

  • ufw — allows the detected SSH port(s) (from sshd -T, falling back to listening sockets, then 22 with a lockout warning) plus 80/443; default deny incoming, default allow outgoing.
  • fail2bansshd jail (systemd backend, 5 retries / 10 min → 1 h ban).
  • unattended-upgrades — daily list refresh + auto security upgrades.
  • sshdPermitRootLogin no, X11Forwarding no, MaxAuthTries 4 via a drop-in; PasswordAuthentication no only when SSH_KEY_ONLY=true. The config is validated with sshd -t and the drop-in is removed automatically if validation fails, to prevent locking yourself out.
  • systemd sandboxingNoNewPrivileges, ProtectSystem=full, ProtectHome, PrivateTmp, scoped ReadWritePaths.
  • Odoo bound to loopback (xmlrpc_interface = 127.0.0.1) so only nginx is exposed.

Prerequisites

  • A fresh Ubuntu 22.04 LTS (jammy) or 24.04 LTS (noble), x86_64 server. (PGDG/wkhtmltopdf package selection assumes these; adjust for other distros.)
  • Run as root (or sudo).
  • Outbound internet (GitHub, PGDG, wkhtmltopdf releases, Let's Encrypt).
  • For TLS to succeed in one shot, point the $DOMAIN DNS A record at the server before running. If it isn't ready, the script still finishes on HTTP and prints the certbot re-run command.

After it runs

list_db = False means there is no web DB-creation wizard, so create the DB from the CLI (the summary prints this):

sudo -u odoo /odoo/venv/bin/python /odoo/odoo-server/odoo-bin \
  -c /etc/odoo-server.conf -d <DB_NAME> -i base --stop-after-init

Then browse to https://$DOMAIN. Service control:

systemctl status odoo
systemctl restart odoo
journalctl -u odoo -n 100 -f

Custom modules go in /odoo/custom_addons/ (already on addons_path).

The auto-generated master password and Postgres db_password are in /root/odoo-install-credentials.txt (mode 0600). Rotate the master password after first login; Odoo can store it as a bcrypt hash in odoo-server.conf.


Quality

Passes shellcheck and bash -n, and runs under set -euo pipefail throughout.

About

One-command Odoo CE installer for Ubuntu: source install, systemd, nginx reverse proxy, Let's Encrypt TLS, and ufw/fail2ban hardening. A Davenport Software project.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages