21 KiB
Odoo Docker Development Environment
This project provides a Docker-based development environment for running Odoo using local Odoo core source code and separate custom addons.
This setup is useful for:
- Odoo custom module development
- Studying Odoo core source code
- Keeping custom addons in a separate Git repository
- Running Odoo and PostgreSQL with Docker Compose
- Managing multiple Odoo versions cleanly
Project Structure
Recommended folder structure:
odoo-dev/
│
├── Dockerfile
├── docker-compose.yml
├── README.md
│
├── config/
│ └── odoo.conf
│
├── odoo/
│ ├── odoo-bin
│ ├── addons/
│ ├── odoo/
│ └── requirements.txt
│
├── custom_addons/
│ └── my_custom_module/
│ ├── __init__.py
│ └── __manifest__.py
│
└── enterprise/
└── optional_enterprise_modules/
Folder Explanation
| Folder/File | Description |
|---|---|
Dockerfile |
Builds the Odoo development image |
docker-compose.yml |
Runs Odoo and PostgreSQL containers |
config/odoo.conf |
Odoo configuration file |
odoo/ |
Local Odoo core source code |
custom_addons/ |
Your custom Odoo modules |
enterprise/ |
Optional Odoo Enterprise addons |
README.md |
Project documentation |
Requirements
Install these tools before starting:
- Docker
- Docker Compose
- Git
- Code editor, for example VS Code or PyCharm
Check installation:
docker --version
docker compose version
git --version
Clone Odoo Source Code
Example for Odoo 17:
git clone https://github.com/odoo/odoo.git --branch 17.0 --depth 1 odoo
Example for Odoo 18:
git clone https://github.com/odoo/odoo.git --branch 18.0 --depth 1 odoo
Example for Odoo 16:
git clone https://github.com/odoo/odoo.git --branch 16.0 --depth 1 odoo
Make sure your Docker image version and Odoo source branch match.
Examples:
Docker image: odoo:17
Odoo source branch: 17.0
Docker image: odoo:18
Odoo source branch: 18.0
Docker image: odoo:16
Odoo source branch: 16.0
Create Custom Addons Folder
Create a folder for your custom modules:
mkdir custom_addons
Example custom addons structure:
custom_addons/
└── my_custom_module/
├── __init__.py
└── __manifest__.py
Dockerfile
Create a file named:
Dockerfile
Use this content:
FROM odoo:17
USER root
RUN apt-get update && apt-get install -y \
git \
nano \
vim \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /opt/odoo
USER odoo
CMD ["python3", "/opt/odoo/odoo-bin", "-c", "/etc/odoo/odoo.conf"]
If you are using Odoo 18, change:
FROM odoo:17
to:
FROM odoo:18
If you are using Odoo 16, change it to:
FROM odoo:16
docker-compose.yml
Create a file named:
docker-compose.yml
Use this content:
services:
db:
image: postgres:15
container_name: odoo_db
environment:
POSTGRES_DB: postgres
POSTGRES_USER: odoo
POSTGRES_PASSWORD: odoo
volumes:
- odoo-db-data:/var/lib/postgresql/data
odoo:
build: .
container_name: odoo_app
depends_on:
- db
ports:
- "8069:8069"
volumes:
- ./odoo:/opt/odoo
- ./custom_addons:/mnt/extra-addons
- ./config/odoo.conf:/etc/odoo/odoo.conf
- odoo-web-data:/var/lib/odoo
command: python3 /opt/odoo/odoo-bin -c /etc/odoo/odoo.conf
volumes:
odoo-db-data:
odoo-web-data:
Odoo Configuration
Create the config folder:
mkdir config
Create the Odoo config file:
config/odoo.conf
Use this content:
[options]
admin_passwd = admin
db_host = db
db_port = 5432
db_user = odoo
db_password = odoo
addons_path = /opt/odoo/addons,/mnt/extra-addons
log_level = info
Using Odoo Enterprise
If you have Odoo Enterprise source code, place it in:
enterprise/
Update docker-compose.yml:
services:
db:
image: postgres:15
container_name: odoo_db
environment:
POSTGRES_DB: postgres
POSTGRES_USER: odoo
POSTGRES_PASSWORD: odoo
volumes:
- odoo-db-data:/var/lib/postgresql/data
odoo:
build: .
container_name: odoo_app
depends_on:
- db
ports:
- "8069:8069"
volumes:
- ./odoo:/opt/odoo
- ./enterprise:/mnt/enterprise
- ./custom_addons:/mnt/extra-addons
- ./config/odoo.conf:/etc/odoo/odoo.conf
- odoo-web-data:/var/lib/odoo
command: python3 /opt/odoo/odoo-bin -c /etc/odoo/odoo.conf
volumes:
odoo-db-data:
odoo-web-data:
Update config/odoo.conf:
[options]
admin_passwd = admin
db_host = db
db_port = 5432
db_user = odoo
db_password = odoo
addons_path = /opt/odoo/addons,/mnt/enterprise,/mnt/extra-addons
log_level = info
Recommended addons path order:
/opt/odoo/addons
/mnt/enterprise
/mnt/extra-addons
Start the Environment
Build and start containers:
docker compose up -d --build
Open Odoo in your browser:
http://localhost:8069
View Odoo logs:
docker compose logs -f odoo
Stop containers:
docker compose down
Stop containers and remove database volumes:
docker compose down -v
Be careful with:
docker compose down -v
It removes your PostgreSQL database data.
Access the Odoo Container
Enter the Odoo container:
docker compose exec odoo bash
Check mounted custom addons:
ls -la /mnt/extra-addons
Check Odoo config:
cat /etc/odoo/odoo.conf
Check local Odoo source:
ls -la /opt/odoo
Exit container:
exit
Access the PostgreSQL Container
Enter the PostgreSQL container:
docker compose exec db bash
Connect to PostgreSQL:
psql -U odoo -d postgres
List databases:
\l
Exit PostgreSQL:
\q
Exit container:
exit
Create a Custom Module
Create a sample custom module:
mkdir -p custom_addons/my_custom_module
touch custom_addons/my_custom_module/__init__.py
touch custom_addons/my_custom_module/__manifest__.py
Example structure:
custom_addons/my_custom_module/
├── __init__.py
└── __manifest__.py
Example __manifest__.py:
{
"name": "My Custom Module",
"version": "17.0.1.0.0",
"category": "Custom",
"summary": "My first custom Odoo module",
"description": """
This is a sample custom module for Odoo development.
""",
"author": "Your Name",
"website": "https://example.com",
"depends": ["base"],
"data": [],
"installable": True,
"application": False,
"license": "LGPL-3",
}
Restart Odoo:
docker compose restart odoo
Then in Odoo:
Settings → Activate Developer Mode
Apps → Update Apps List
Search for:
My Custom Module
Important:
Remove the default Apps filter from the search bar if your module does not appear.
Example Custom Model Module
Create this structure:
custom_addons/my_custom_module/
├── __init__.py
├── __manifest__.py
├── models/
│ ├── __init__.py
│ └── custom_model.py
└── views/
└── custom_model_views.xml
Create folders:
mkdir -p custom_addons/my_custom_module/models
mkdir -p custom_addons/my_custom_module/views
touch custom_addons/my_custom_module/models/__init__.py
Update root __init__.py:
from . import models
Update models/__init__.py:
from . import custom_model
Create models/custom_model.py:
from odoo import models, fields
class CustomModel(models.Model):
_name = "custom.model"
_description = "Custom Model"
name = fields.Char(string="Name", required=True)
description = fields.Text(string="Description")
active = fields.Boolean(string="Active", default=True)
Create views/custom_model_views.xml:
<odoo>
<record id="view_custom_model_tree" model="ir.ui.view">
<field name="name">custom.model.tree</field>
<field name="model">custom.model</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="description"/>
<field name="active"/>
</tree>
</field>
</record>
<record id="view_custom_model_form" model="ir.ui.view">
<field name="name">custom.model.form</field>
<field name="model">custom.model</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
<field name="description"/>
<field name="active"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_custom_model" model="ir.actions.act_window">
<field name="name">Custom Models</field>
<field name="res_model">custom.model</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_custom_root"
name="Custom App"
sequence="10"/>
<menuitem id="menu_custom_model"
name="Custom Models"
parent="menu_custom_root"
action="action_custom_model"
sequence="10"/>
</odoo>
Update __manifest__.py:
{
"name": "My Custom Module",
"version": "17.0.1.0.0",
"category": "Custom",
"summary": "My first custom Odoo module",
"author": "Your Name",
"depends": ["base"],
"data": [
"views/custom_model_views.xml",
],
"installable": True,
"application": True,
"license": "LGPL-3",
}
Upgrade the module:
docker compose exec odoo python3 /opt/odoo/odoo-bin \
-c /etc/odoo/odoo.conf \
-d your_database_name \
-u my_custom_module \
--stop-after-init
Restart Odoo:
docker compose restart odoo
Git Setup
Recommended Git structure:
odoo-dev/
├── odoo/ # Odoo core repository
├── custom_addons/ # Your custom addons repository
├── enterprise/ # Optional Enterprise repository
├── Dockerfile
├── docker-compose.yml
└── config/
Odoo Core Remote
Check the remote:
cd odoo
git remote -v
Example result:
origin https://github.com/odoo/odoo.git
Custom Addons Remote
Initialize Git inside custom_addons:
cd custom_addons
git init
git add .
git commit -m "Initial custom addons"
git branch -M main
git remote add origin git@github.com:yourname/odoo-custom-addons.git
git push -u origin main
This keeps Odoo core and your custom modules separate.
Do not put your custom modules inside:
odoo/addons/
Recommended:
custom_addons/
Addons Path
The addons_path tells Odoo where to find modules.
Example without Enterprise:
addons_path = /opt/odoo/addons,/mnt/extra-addons
Example with Enterprise:
addons_path = /opt/odoo/addons,/mnt/enterprise,/mnt/extra-addons
Make sure the paths match your Docker volume mounts.
Example:
volumes:
- ./custom_addons:/mnt/extra-addons
Then odoo.conf must include:
/mnt/extra-addons
If your compose file has:
- ./custom_addons:/mnt/custom-addons
Then your config must use:
/mnt/custom-addons
The names must match exactly.
Common Commands
Start Containers
docker compose up -d
Build and Start Containers
docker compose up -d --build
Stop Containers
docker compose down
Stop Containers and Delete Volumes
docker compose down -v
Restart Odoo
docker compose restart odoo
View Odoo Logs
docker compose logs -f odoo
View Database Logs
docker compose logs -f db
Enter Odoo Container
docker compose exec odoo bash
Enter PostgreSQL Container
docker compose exec db bash
Check Mounted Custom Addons
docker compose exec odoo ls -la /mnt/extra-addons
Check Odoo Config
docker compose exec odoo cat /etc/odoo/odoo.conf
Upgrade a Module
Replace your_database_name and your_module_name:
docker compose exec odoo python3 /opt/odoo/odoo-bin \
-c /etc/odoo/odoo.conf \
-d your_database_name \
-u your_module_name \
--stop-after-init
Then restart:
docker compose restart odoo
Install a Module from Command Line
Replace your_database_name and your_module_name:
docker compose exec odoo python3 /opt/odoo/odoo-bin \
-c /etc/odoo/odoo.conf \
-d your_database_name \
-i your_module_name \
--stop-after-init
Then restart:
docker compose restart odoo
Database Management
List Databases
docker compose exec db psql -U odoo -d postgres -c "\l"
Backup Database
Replace your_database_name:
docker compose exec db pg_dump -U odoo your_database_name > backup.sql
Restore Database
Create database first if needed:
docker compose exec db createdb -U odoo restored_database
Restore:
cat backup.sql | docker compose exec -T db psql -U odoo restored_database
Development Workflow
Recommended workflow:
1. Start Docker containers
docker compose up -d
2. Create or edit modules in custom addons
custom_addons/
3. Restart Odoo if Python files changed
docker compose restart odoo
4. Update Apps List in Odoo
In the Odoo web interface:
Settings → Activate Developer Mode
Apps → Update Apps List
5. Upgrade your module
docker compose exec odoo python3 /opt/odoo/odoo-bin \
-c /etc/odoo/odoo.conf \
-d your_database_name \
-u your_module_name \
--stop-after-init
6. Restart Odoo again
docker compose restart odoo
Updating Apps List
In Odoo UI:
Settings → Activate Developer Mode
Apps → Update Apps List
Then search your module.
Important:
Remove the default search filter:
Apps
Sometimes custom technical modules do not appear because the default Apps filter is active.
Troubleshooting
Problem: /mnt/extra-addons is not shown
Check the volume mount in docker-compose.yml:
volumes:
- ./custom_addons:/mnt/extra-addons
Check inside the container:
docker compose exec odoo ls -la /mnt/extra-addons
If it is empty, check that your local custom_addons folder contains modules.
Problem: Custom module does not appear in Apps
Check these items.
1. Is the folder mounted?
docker compose exec odoo ls -la /mnt/extra-addons
2. Is the path in odoo.conf?
docker compose exec odoo cat /etc/odoo/odoo.conf
Expected:
addons_path = /opt/odoo/addons,/mnt/extra-addons
3. Does the module have __manifest__.py?
Valid:
custom_addons/my_module/__manifest__.py
Invalid:
custom_addons/my_module/something_else/__manifest__.py
unless your addons_path points to the correct parent folder.
4. Is the module installable?
In __manifest__.py:
"installable": True
5. Did you update Apps List?
In Odoo:
Apps → Update Apps List
6. Did you remove the default Apps filter?
In the Apps search bar, remove:
Apps
7. Check logs
docker compose logs -f odoo
Problem: Docker build fails on pip3 install
If you use the official Odoo Docker image, do not reinstall the full Odoo core requirements.txt.
Avoid this:
COPY ./odoo/requirements.txt /tmp/requirements.txt
RUN pip3 install --break-system-packages -r /tmp/requirements.txt
Usually, this Dockerfile is enough:
FROM odoo:17
USER root
RUN apt-get update && apt-get install -y \
git \
nano \
vim \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /opt/odoo
USER odoo
CMD ["python3", "/opt/odoo/odoo-bin", "-c", "/etc/odoo/odoo.conf"]
If your custom modules need extra Python packages, create:
requirements-extra.txt
Example:
requests
openpyxl
boto3
Then update Dockerfile:
FROM odoo:17
USER root
RUN apt-get update && apt-get install -y \
git \
nano \
vim \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
COPY ./requirements-extra.txt /tmp/requirements-extra.txt
RUN pip3 install --break-system-packages --no-cache-dir -r /tmp/requirements-extra.txt
WORKDIR /opt/odoo
USER odoo
CMD ["python3", "/opt/odoo/odoo-bin", "-c", "/etc/odoo/odoo.conf"]
Problem: Odoo image version and source version mismatch
Make sure these match:
FROM odoo:17
with:
Odoo source branch 17.0
Check branch:
cd odoo
git branch
Switch branch:
git checkout 17.0
For Odoo 18:
FROM odoo:18
Use branch:
git checkout 18.0
Problem: Database connection error
Check config/odoo.conf:
db_host = db
db_port = 5432
db_user = odoo
db_password = odoo
Check docker-compose.yml:
db:
environment:
POSTGRES_USER: odoo
POSTGRES_PASSWORD: odoo
The database user and password must match.
Problem: Permission issue in custom addons
Check file permissions:
ls -la custom_addons
On Linux or macOS, you may fix ownership with:
sudo chown -R $USER:$USER custom_addons
Then restart:
docker compose restart odoo
Problem: Changes in Python files are not applied
Restart Odoo:
docker compose restart odoo
Then upgrade the module:
docker compose exec odoo python3 /opt/odoo/odoo-bin \
-c /etc/odoo/odoo.conf \
-d your_database_name \
-u your_module_name \
--stop-after-init
Restart again:
docker compose restart odoo
Problem: XML view changes are not applied
Upgrade your module:
docker compose exec odoo python3 /opt/odoo/odoo-bin \
-c /etc/odoo/odoo.conf \
-d your_database_name \
-u your_module_name \
--stop-after-init
Then restart:
docker compose restart odoo
Recommended Best Practices
Keep Odoo Core Clean
Do not modify Odoo core unless absolutely necessary.
Avoid editing files inside:
odoo/addons/
odoo/odoo/
Instead, create custom modules inside:
custom_addons/
Use inheritance instead of changing core files.
Example:
from odoo import models, fields
class SaleOrder(models.Model):
_inherit = "sale.order"
x_custom_note = fields.Char(string="Custom Note")
Use Separate Git Repositories
Recommended:
odoo/ → Odoo official source code
custom_addons/ → Your custom addons repository
enterprise/ → Odoo Enterprise source code, optional
This makes upgrades and maintenance easier.
Use Version-Specific Folders
Example:
odoo16-dev/
odoo17-dev/
odoo18-dev/
Each project can have its own:
Dockerfile
docker-compose.yml
odoo.conf
custom_addons
database volume
Use Meaningful Module Names
Good module names:
sale_order_customization
stock_barcode_extension
school_management
account_report_custom
Avoid unclear names:
test
module1
custom
new_module
Use Git Frequently
Inside custom_addons:
git status
git add .
git commit -m "Add sale order customization"
git push
Full Example Final Files
Dockerfile
FROM odoo:17
USER root
RUN apt-get update && apt-get install -y \
git \
nano \
vim \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /opt/odoo
USER odoo
CMD ["python3", "/opt/odoo/odoo-bin", "-c", "/etc/odoo/odoo.conf"]
docker-compose.yml
services:
db:
image: postgres:15
container_name: odoo_db
environment:
POSTGRES_DB: postgres
POSTGRES_USER: odoo
POSTGRES_PASSWORD: odoo
volumes:
- odoo-db-data:/var/lib/postgresql/data
odoo:
build: .
container_name: odoo_app
depends_on:
- db
ports:
- "8069:8069"
volumes:
- ./odoo:/opt/odoo
- ./custom_addons:/mnt/extra-addons
- ./config/odoo.conf:/etc/odoo/odoo.conf
- odoo-web-data:/var/lib/odoo
command: python3 /opt/odoo/odoo-bin -c /etc/odoo/odoo.conf
volumes:
odoo-db-data:
odoo-web-data:
config/odoo.conf
[options]
admin_passwd = admin
db_host = db
db_port = 5432
db_user = odoo
db_password = odoo
addons_path = /opt/odoo/addons,/mnt/extra-addons
log_level = info
Final Notes
This setup runs Odoo from your local source code inside Docker.
Local Odoo source:
./odoo → /opt/odoo
Custom addons:
./custom_addons → /mnt/extra-addons
Odoo detects custom modules using:
addons_path = /opt/odoo/addons,/mnt/extra-addons
After creating or modifying modules:
docker compose restart odoo
Then update the Apps List from the Odoo interface.
For normal Odoo development, always prefer custom addons over editing Odoo core directly.