Odoo
Odoo Community: open-source ERP and CRM
Odoo on Cubeship
Odoo is an open-source suite of business apps: CRM, sales, invoicing and accounting, inventory, projects, a website builder and more, all sharing one database.
This template installs Odoo Community on a Cubeship instance, with the managed Postgres it keeps everything in and a volume for the files it stores beside it.
What it creates
- odoo — Odoo Community 19.0, built on the instance from the
Dockerfilein this repository on top ofodoo:19.0-20260908. It answers on the domain you choose and keeps attachments and sessions in a volume at/var/lib/odoo. - odoo-db — a managed Postgres 17, with a login called
odooand a database calledodoo.
It needs Cubeship 0.7.0 or newer, and an admin to install it: the app is built on the instance, and only admins build.
Why it is built
The published image, odoo, starts with no database. On a first visit it
opens its database manager, a public page where whoever arrives first creates
one. A database made there would also not be the one Cubeship backs up:
Cubeship dumps the database it created, odoo, and Odoo would make another.
So the Dockerfile here is that image with two additions:
odoo.conf: proxy mode on, the threaded server, and the database manager off.cubeship-entrypoint.sh, which runs before the image's own entrypoint. At every start it writes the master password intoodoo.conf— Odoo reads it from that file and nowhere else. On the first start, while theodoodatabase is still empty, it creates it withodoo db init: your language and country, your login and password, and no demo data. It never replaces a database that has tables in it.
What you are asked
| Input | What to give |
|---|---|
| Where Odoo answers | A domain you control, pointed at your instance. |
| The email you sign in with | Your login as Odoo's administrator. It also becomes the company's email address. |
| The password you sign in with | Nothing — the instance generates it and shows it once. It is used when the database is created, and never again: change it in Odoo afterwards, not in the app's settings. |
| Odoo's master password | Nothing — generated and shown once. It guards creating, copying, dumping and deleting databases. With the database manager off nothing asks for it, but keep it for the day you turn the manager on. |
| The language the database is created in | An Odoo language code: en_US, fr_FR, pt_BR, es_419… More languages can be installed later. |
| The company's country | Optional. A two-letter code, like US or BR. It sets the company's currency, and everyone's timezone when the country has only one. Leave it empty to set it later. |
A language or country Odoo does not know makes the first start fail: the database is dropped again and the app keeps restarting. Correct the variable in the app's settings and deploy.
After installing
- Wait a few minutes. The first start creates the database and installs
Odoo's base modules, and the domain answers
503until that is done. The app's logs sayCreating Odoo database odoowhen it starts, and Odoo's own log follows. - Open the domain and sign in with the email and generated password.
- Change the password under your avatar → My Preferences → Security.
- Open Apps and install what you need. Set the company's country under Settings → Companies before installing Invoicing or Accounting: the chart of accounts and taxes are picked from it.
- Set up an outgoing mail server in Settings. Odoo sends no email until you do.
Cubeship has no console into an app. Anything that needs a shell is done over
SSH on the machine the app runs on, with docker exec. To reset the
administrator's password, for instance:
docker exec -it $(docker ps -qf name=cubeship-odoo-production-odoo) \
odoo shell --no-http
env['res.users'].browse(2).password = 'a new password'
env.cr.commit()
The container name follows the project, environment and app names you install
with. browse(2) is the administrator the database was created with.
Choices this template makes
-
The threaded server, not workers. Odoo's live features — chat, notifications, a record updating while another person edits it — go over a websocket at
/websocket. Withworkersset, Odoo serves that on a second port,8072, and expects a proxy to send/websocketthere. A Cubeship domain reaches one port. The threaded server (workers = 0) serves the websocket on8069with everything else, so it all works on one domain.The cost is the one Odoo's deployment guide describes: one Python process, so heavy requests share one CPU core, and no per-worker memory or time limits recycling a runaway request. It suits a company of a few dozen people. Beyond that, Odoo's multi-process server wants a proxy that Cubeship cannot configure for one app.
-
No database manager.
list_db = Falseturns off the page at/web/database/managerand the database calls of the XML-RPC API, as Odoo's guide advises once an instance serves one database. The database is named byPGDATABASE, and Odoo serves only that one. -
Proxy mode on. TLS ends at Cubeship's proxy, and Odoo needs
proxy_mode = Trueto believe theX-Forwarded-*headers — otherwise its links arehttp://and every login is logged from the proxy's address. -
No demo data. To try Odoo with sample records, install a separate copy; demo data cannot be removed from a database once loaded.
-
The database login is a superuser. Cubeship's managed Postgres gives its one login every privilege, and Odoo's guide says its login should not be a superuser. Odoo only refuses the name
postgres, so it runs. The Postgres server is Odoo's alone.
Backups
Odoo keeps every record in the database, and the files attached to them —
attachments, images, the compiled web assets — in the volume, under
/var/lib/odoo/filestore/odoo. A backup needs both, taken close together:
a database restored without its filestore shows broken images and missing
attachments.
- Back the database up from odoo-db's page in the dashboard.
- Back the volume up from the app's settings.
Restore both, then deploy the app.
The volume
The app runs as one copy on the machine its volume is on, and a deploy stops
it for a few seconds: sessions survive, open pages reconnect. Custom modules
are not in the volume — /mnt/extra-addons is empty and disappears with each
container. To add modules, fork this repository and COPY them into
/mnt/extra-addons in the Dockerfile.
Updating
Odoo publishes a dated image for every nightly build of 19.0. Change the tag in
the Dockerfile, release this repository — or your fork — and point the app's
ref at the new release. Back up the database and the volume first. After a
deploy, update the installed modules if the release notes ask for it:
docker exec -it $(docker ps -qf name=cubeship-odoo-production-odoo) \
odoo -u all --stop-after-init --no-http
Moving to a new major version (20.0) is a migration, not a tag change: Odoo Community has no upgrade path of its own; OpenUpgrade is the community's.
Resources
The app is limited to 2 CPUs and 2 GiB of memory; the threaded server uses one
core for Python work at a time. Raise limits in template.yaml for many
users or large imports.
What this creates
odoo
https://github.com/cubeshipd/cubeship-odoo-template
odoo-db
Postgres 17
/var/lib/odoo
Volume of odoo
# yaml-language-server: $schema=https://cubeship.dev/schema/template/v1.json
version: 1
# The first release that gives a volume to the user its image runs as;
# Odoo runs as odoo and cannot write to one owned by root.
minCubeship: "0.7.0"
project: odoo
inputs:
- key: domain
type: domain
label: Where Odoo answers
- key: adminLogin
type: text
label: The email you sign in with
help: Also set as the company's email address.
pattern: ^[^@\s]+@[^@\s]+\.[^@\s]+$
- key: adminPassword
type: secret
label: The password you sign in with
help: Read once, when the database is created. Change it in Odoo afterwards — changing it here does nothing.
generate: 24
- key: masterPassword
type: secret
label: Odoo's master password
help: Guards creating, copying, dumping and deleting databases. The database manager is off; keep this for the day you turn it on.
generate: 32
- key: language
type: text
label: The language the database is created in
help: An Odoo language code, like en_US, fr_FR, pt_BR or es_419. More can be installed later.
default: en_US
pattern: '[a-z]{2,3}(_[A-Z]{2}|_[0-9]{3})?(@[a-z]+)?'
- key: country
type: text
label: The company's country
help: A two-letter code, like US, FR or BR. Sets the company's currency and, for a country with one timezone, everyone's timezone. Leave it empty to set it later.
required: false
# An unanswered optional input is still checked, as an empty string.
pattern: '([A-Z]{2})?'
databases:
- key: db
name: odoo-db
engine: postgres
version: "17"
# Anything but `postgres`, which Odoo refuses to run as.
username: odoo
database: odoo
apps:
- key: web
name: odoo
# The published image creates no database: it expects someone to make
# one in its web database manager, which would be open on the domain
# and would not be the database Cubeship backs up. The Dockerfile in
# this repository is that image creating this one on first start, with
# the manager off and proxy mode on.
repo: https://github.com/cubeshipd/cubeship-odoo-template
ref: v1.0.0
build: dockerfile
# Threaded mode: the websocket is on this port too. With workers it
# moves to 8072, and a domain reaches one port.
port: 8069
health: /web/health
domains:
- host: ${input.domain}
attach:
- database: db
volumes:
- path: /var/lib/odoo
limits: { cpu: 2, memory: 2Gi }
env:
# Odoo and psql both read libpq's own variables, and with them set the
# image's entrypoint adds no connection flags of its own.
PGHOST: ${db.db.host}
PGPORT: ${db.db.port}
PGUSER: ${db.db.user}
PGPASSWORD: ${db.db.password}
PGDATABASE: ${db.db.name}
ODOO_MASTER_PASSWORD: ${input.masterPassword}
ODOO_ADMIN_LOGIN: ${input.adminLogin}
ODOO_ADMIN_PASSWORD: ${input.adminPassword}
ODOO_LANGUAGE: ${input.language}
ODOO_COUNTRY: ${input.country}