This repository contains a custom Odoo 10.0 Docker image with a robust bootstrap/entrypoint system designed for long-running environments and legacy Odoo installations.
The image is intended for users who need:
- Odoo 10 (Python 2.7, Node 6)
- Git-aggregated source code
- Repeatable container bootstrapping
- Explicit control over upgrades and initialization
ghcr.io/<org>/odoo-10.0
Note: Replace <org> with your organization name (e.g., nuobit, mycompany, etc.)
1.0.0,1.0.1, etc. — immutable release versionsedge— latest build from main branch (moving tag, may break)
Recommended: always pin to a specific numbered tag in production (e.g., 1.0.0).
Tag strategy:
- Numbered versions (
1.0.0): Stable, tested releases that never change edge: Bleeding edge, rebuilt on every commit - use only for testing
- Base OS: Debian Stretch (EOL, using
archive.debian.org) - Python: 2.7
- Node.js: 6.x
- wkhtmltopdf: 0.12.1.4 (static Debian package)
- User: non-root (
odoo, uid99910)
This image is legacy by design and intended for environments that must keep Odoo 10 running.
The container uses a fixed UID 99910 for the odoo user, deliberately hardcoded in both the Dockerfile and docker-compose configuration.
Instead of letting Docker auto-assign UIDs, we explicitly control the user ID to ensure:
- Predictable ownership: Files created by the container always have the same UID across all environments
- Dockerfile-compose consistency: The UID in the image matches the UID specified in
user:directive - Manageable permissions: Administrators know exactly which UID to
chownon host directories
- Arbitrary but intentional: We chose a high, arbitrary UID outside typical ranges
- High UID range (90000+): Avoids collision with:
- System users (typically < 1000)
- Regular host users (typically 1000-60000)
- Service users (typically 60000-90000)
- Version indicator: The
10suffix represents Odoo version 10, making it identifiable - Consistent across deployments: Same UID in development, staging, and production
Without a fixed UID, Docker might assign UID 1000 to the container user. If your host user also has UID 1000:
- Files created by the container appear to belong to your host user
- Your host user can accidentally modify container-owned files
- Permissions become confusing and unpredictable
- Different environments might get different UIDs, breaking reproducibility
With a fixed high UID (99910), there's no collision, and ownership is always clear.
You do NOT need to create this user on the host. Linux uses numeric UIDs, not usernames. The container's odoo user (UID 99910) can access files owned by UID 99910 on the host, regardless of username.
Set ownership on host bind-mounted directories:
# Example for a container (could be odoo10-1, odoo10-customer1, etc.)
sudo chown -R 99910:99910 /srv/docker/data/odoo10-1Restrict permissions for security:
sudo chmod -R o-rwx /srv/docker/data/odoo10-1Note: Both the container name (odoo10-1) and host path (/srv/docker/data/) are arbitrary examples. You can use any naming convention and path structure that suits your environment:
- Container:
odoo10-customer1,odoo10-prod, etc. - Host path:
/srv/docker/data/,/opt/containers/,/home/user/docker/, etc.
You can run multiple Odoo 10 containers on the same host (e.g., odoo10-1, odoo10-2, odoo10-customer1), all using the same UID 99910.
This is NOT a problem as long as:
- ✅ Each container has its own separate bind-mounted directories
- ✅ Directories are named clearly to match the container (e.g.,
/srv/docker/data/odoo10-1,/srv/docker/data/odoo10-2)
What to avoid:
- ❌ Never share the same data directory between multiple containers
- ❌ Don't bind
/srv/docker/data/odoo-sharedto bothodoo10-1andodoo10-2
Why this works:
- Linux permissions are per-file, not per-user
- Multiple containers with UID 99910 can coexist peacefully
- Each container only accesses its own mounted directories
- File conflicts are impossible when directories are separate
Example multi-container setup:
# Container 1
sudo chown -R 99910:99910 /srv/docker/data/odoo10-1
sudo chmod -R o-rwx /srv/docker/data/odoo10-1
# Container 2 (different directory, same UID - no problem!)
sudo chown -R 99910:99910 /srv/docker/data/odoo10-2
sudo chmod -R o-rwx /srv/docker/data/odoo10-2This ensures:
- Container can read/write bind-mounted volumes
- Files have consistent ownership across environments
- No confusion with existing host users
- Other users cannot access sensitive data
- Multiple containers coexist without permission conflicts
Container internal paths:
| Container Path | Repository File | Purpose |
|---|---|---|
/opt/odoo/entrypoint.sh |
entrypoint.sh |
Container entrypoint |
/opt/odoo/.local/lib/common.sh |
lib/common.sh |
Shared functions and variables |
/opt/odoo/.local/bin/initdb |
scripts/initdb.sh |
Initialize database script |
/opt/odoo/.local/bin/updatemodules |
scripts/updatemodules.sh |
Update modules script |
/opt/odoo/.local/bin/shell |
scripts/shell.sh |
Interactive shell script |
/opt/odoo/.local/bin/fetchbasereqs |
scripts/fetchbasereqs.sh |
Fetch base requirements script |
/opt/odoo/.local/bin/fetchreqs |
scripts/fetchreqs.sh |
Fetch repo requirements script |
/opt/odoo/.local/bin/fetchcode |
scripts/fetchcode.sh |
Fetch git repositories script |
/opt/odoo/.local/bin/dbctl |
scripts/dbctl.sh |
Database management script |
/etc/odoo/constraints.txt |
constraints.txt |
Python package version constraints |
/opt/odoo/pfbfer.zip |
pfbfer.zip |
ReportLab Type1 fonts archive |
Runtime directories (created at runtime or via volume mounts):
| Path | Purpose |
|---|---|
/opt/odoo/src |
Odoo source code (git-aggregated, typically volume-mounted) |
/var/lib/odoo |
Odoo data directory (filestore, sessions, typically volume-mounted) |
/etc/odoo/odoo.conf |
Odoo configuration file (typically volume-mounted) |
Note: Paths shown are inside the container. Use volume mounts in docker-compose to map host directories to container paths.
The container uses a stateful bootstrap mechanism:
- On first container start:
- fetches base Python requirements
- fetches git repositories using
git-aggregator - fetches Odoo Python requirements
- installs ReportLab Type1 fonts
- On subsequent restarts:
- skips bootstrap
- runs Odoo directly
Bootstrap state is stored in:
/var/lib/odoo/.bootstrap/
These scripts can be executed inside the running container:
| Script | Description | Usage |
|---|---|---|
initdb |
Initialize database | docker compose exec <container> initdb <database> [nodemo] |
updatemodules |
Update modules | docker compose exec <container> updatemodules <database> <all|module_list|changed> |
shell |
Interactive Odoo shell | docker compose exec <container> shell <database> |
fetchbasereqs |
Fetch base requirements | docker compose exec <container> fetchbasereqs |
fetchreqs |
Fetch repo requirements | docker compose exec <container> fetchreqs <repo_path> |
fetchcode |
Fetch git repositories | docker compose exec <container> fetchcode [addon_path] [jobs] |
dbctl |
Database management | docker compose exec <container> dbctl <command> [args...] |
The dbctl script provides database management commands:
# Create a database with unaccent extension
docker compose exec <container> dbctl create <database>
# Create a database with specific owner
docker compose exec <container> dbctl create <database> <owner>
# Drop a database
docker compose exec <container> dbctl drop <database>
# Reset (drop and recreate) a database
docker compose exec <container> dbctl reset <database> [owner]
# Create a PostgreSQL user
docker compose exec <container> dbctl createuser <username> [password]Option 1: Interactive password prompt (most secure)
docker compose exec -it <container> dbctl create mydb
# You will be prompted for the PostgreSQL passwordOption 2: Environment variable (less secure, visible in process list)
docker compose exec -e PGPASSWORD=yourpass <container> dbctl create mydbOption 3: .pgpass file (recommended for automation)
Mount a .pgpass file in your docker-compose:
volumes:
- ./.pgpass:/opt/odoo/.pgpass:roFormat of .pgpass:
hostname:port:database:username:password
# Example:
db:5432:*:postgres:yourpass
Set permissions: chmod 600 .pgpass
Option 4: Docker environment variables (set in docker-compose)
environment:
PGHOST: db
PGUSER: postgres
PGPASSWORD: yourpassDefault connection parameters:
PGHOST:db(typical docker-compose database service name)PGUSER:postgresPGPASSWORD: Not set (will prompt if not provided)
Interactive shell access:
docker compose exec -it <container> bashRunning Python scripts via stdin:
When piping a script to shell, use the -T flag:
docker compose exec -T <container> shell <database> < myscript.pyservices:
odoo10:
image: ghcr.io/<org>/odoo-10.0:1.0.0
container_name: odoo10
user: "99910:99910"
ports:
- "127.0.0.1:8010:8069"
volumes:
- ./odoo.conf:/etc/odoo/odoo.conf:ro
- ./src:/opt/odoo/src
- ./repos.yaml:/opt/odoo/src/repos.yaml:ro
- ./data:/var/lib/odoo
networks:
- odoo-net
networks:
odoo-net:
external: trueNote: This example uses relative paths (./odoo.conf, ./src, ./data) for simplicity. In production, consider using absolute paths for clarity:
- Config:
/srv/docker/stack/odoo10/odoo.conf - Source:
/srv/docker/data/odoo10/src - Data:
/srv/docker/data/odoo10/data
Directory structure convention:
/srv/docker/
├── stack/ # Configuration files
│ ├── odoo10-1/ # Container-specific folder
│ │ ├── odoo.conf
│ │ ├── repos.yaml
│ │ └── docker-compose.yml
│ └── odoo10-2/ # Another container
│ └── ...
└── data/ # Runtime data
├── odoo10-1/ # Container-specific folder
│ ├── src/ # Source code
│ └── data/ # Database filestore, sessions, etc.
└── odoo10-2/
└── ...
-
/srv/docker/stack/<container-name>/— Configuration files- Read-only files that define how the container runs
- Usually version-controlled and backed up separately
- Examples: odoo.conf, repos.yaml, docker-compose.yml
-
/srv/docker/data/<container-name>/— Runtime data- Large, frequently changing data
- Requires regular backups
- Not typically version-controlled
- Examples: src/ (git repos), data/ (Odoo filestore)
Note: The /srv/docker/ base path and the folder names are arbitrary conventions—use any directory structure that fits your organization's standards.
| Variable | Default | Description |
|---|---|---|
DAT |
/var/lib/odoo |
Data directory |
SRC |
/opt/odoo/src |
Source directory |
CONF_BASE |
/etc/odoo |
Config base path |
CONF |
/etc/odoo/odoo.conf |
Odoo config file |
SRC_REPOS_FILENAME |
repos.yaml |
Git-aggregator config |
ODOO_BIN |
odoo-bin |
Odoo executable |
PYTHON |
python |
Python interpreter |
docker build -t odoo-10.0:local .Note: Replace <org> with your organization name in all commands below.
# Build with edge tag
docker build -t ghcr.io/<org>/odoo-10.0:edge .
# Push to registry
docker push ghcr.io/<org>/odoo-10.0:edge# Build with version tag (always 3 numbers: MAJOR.MINOR.PATCH)
docker build -t ghcr.io/<org>/odoo-10.0:1.0.0 .
# Push the tag
docker push ghcr.io/<org>/odoo-10.0:1.0.0-
Always use semantic versioning with 3 numbers:
MAJOR.MINOR.PATCH- Example:
1.0.0,1.2.5,2.0.0 MAJOR(1st number): Breaking changes, incompatible updatesMINOR(2nd number): New features, backwards compatiblePATCH(3rd number): Bug fixes only, no new features- Never use fewer than 3 numbers - always use the format
X.Y.Z
- Example:
-
Production recommendations:
- ✅ Always pin to full 3-number version:
ghcr.io/<org>/odoo-10.0:1.0.0 - ❌ Never use
edgein production - ❌ Never use short versions like
1.0or1
- ✅ Always pin to full 3-number version:
-
Test before tagging:
- Build and test locally first
- Only push to registry after validation
- Tag releases from tested commits only
- Debian Stretch and Python 2.7 are EOL
- TLS / CA issues may occur in restricted networks
- Internet access is required at first bootstrap (pip, fonts)
- This image is not suitable for new Odoo deployments
This image is provided as-is for legacy compatibility. Odoo is a trademark of Odoo S.A.
Use at your own risk.