Odoo Service Skill
- Overview / Role
The odoo-service skill is the complete Odoo server lifecycle manager. It handles every aspect of running, deploying, initializing, and managing Odoo servers across ANY environment and ANY IDE.
What This Skill Does
-
Server Management: Start, stop, restart Odoo processes across Windows, Linux, and macOS
-
Environment Detection: Automatically detects whether you are in a local venv, Docker, or bare Python environment
-
Environment Initialization: Creates virtual environments, installs dependencies, sets up PostgreSQL, generates config files
-
Database Management: Backup, restore, create, drop, list databases; reset admin passwords
-
Docker Orchestration: Generate Dockerfiles, docker-compose files, manage containers, exec into shells
-
IDE Configuration: Generate PyCharm run configurations and VSCode tasks/launch/settings files
-
Module Management: Install, update, scaffold Odoo modules from CLI or plugin commands
-
Multi-Project Support: Manage multiple Odoo projects with different configs, ports, and databases simultaneously
Supported Odoo Versions
Version Status Python Range Notes
14 Supported 3.7 - 3.10 LTS, legacy projects
15 Supported 3.8 - 3.11 Community projects
16 Supported 3.9 - 3.12 Active enterprise
17 Supported 3.10 - 3.13 Primary development version
18 Supported 3.10 - 3.13 Active new projects
19 Supported 3.10 - 3.13 Latest features
Quick Decision Matrix: Which Environment?
Are you deploying to production? YES → Docker (isolation, reproducibility, easy scaling) NO → Do you have an existing venv/ or .venv/ directory? YES → Local venv (keep using it) NO → Do you want isolation from system Python? YES → Local venv (recommended for dev) NO → Bare Python (simplest, least isolation)
- Environment Detection Logic
The skill auto-detects the running environment using the following priority order:
Detection Algorithm
import os from pathlib import Path
def detect_environment(project_root: str) -> str: """ Detect which Odoo environment is active. Returns: 'venv', 'docker', or 'bare' Priority: docker > venv > bare """ root = Path(project_root)
# 1. Check for Docker environment (highest priority)
if (root / 'docker-compose.yml').exists():
return 'docker'
if (root / 'docker-compose.yaml').exists():
return 'docker'
if (root / 'Dockerfile').exists():
return 'docker'
# 2. Check for virtual environment
venv_dirs = ['.venv', 'venv', 'env', '.env']
for venv_dir in venv_dirs:
venv_path = root / venv_dir
if venv_path.is_dir():
# Verify it's actually a venv (has python executable)
if (venv_path / 'Scripts' / 'python.exe').exists(): # Windows
return 'venv'
if (venv_path / 'bin' / 'python').exists(): # Linux/Mac
return 'venv'
# 3. Fall back to bare Python
return 'bare'
Detection Signals
-
.venv/Scripts/python.exe (Windows) or .venv/bin/python (Linux/Mac) → local-venv
-
docker-compose.yml or docker-compose.yaml in project root → docker
-
Dockerfile only (no compose) → docker-build mode
-
None of the above → bare-python
Override Detection
You can force an environment by passing flags:
-
--env venv — force local venv
-
--env docker — force Docker
-
--env bare — force bare Python
-
--docker — shorthand for --env docker
- Local Virtual Environment Setup
Windows Setup (Step-by-Step)
Step 1: Navigate to your Odoo version directory
cd C:\TQ-WorkSpace\odoo\odoo17
Step 2: Create virtual environment
python -m venv .venv
Step 3: Activate
..venv\Scripts\activate
Step 4: Upgrade pip
python -m pip install --upgrade pip
Step 5: Install core Odoo requirements
pip install -r requirements.txt
Step 6: Install project-specific requirements (if exists)
pip install -r projects\myproject\requirements.txt
Step 7: Verify installation
python -c "import odoo; print('Odoo importable')"
Linux / macOS Setup
Step 1: Navigate to Odoo directory
cd /opt/odoo17
Step 2: Create virtual environment with specific Python version
python3.10 -m venv .venv
Step 3: Activate
source .venv/bin/activate
Step 4: Upgrade pip
pip install --upgrade pip
Step 5: Install requirements
pip install -r requirements.txt
Step 6: Project-specific requirements
pip install -r projects/myproject/requirements.txt
Step 7: Verify
python -c "import odoo; print('Odoo importable')"
Python Version Requirements Table
Odoo Version Min Python Max Python Recommended Notes
14 3.7 3.10 3.8 EOL versions, be careful with libraries
15 3.8 3.11 3.9 Stable range
16 3.9 3.12 3.10 Wider support
17 3.10 3.13 3.11 Primary dev version
18 3.10 3.13 3.11 New projects
19 3.10 3.13 3.12 Latest features
Verifying Python Version Compatibility
import sys
def check_python_compatibility(odoo_version: int) -> bool: """Check if current Python is compatible with given Odoo version.""" version_ranges = { 14: ((3, 7), (3, 10)), 15: ((3, 8), (3, 11)), 16: ((3, 9), (3, 12)), 17: ((3, 10), (3, 13)), 18: ((3, 10), (3, 13)), 19: ((3, 10), (3, 13)), }
current = sys.version_info[:2]
min_ver, max_ver = version_ranges.get(odoo_version, ((3, 10), (3, 13)))
return min_ver <= current <= max_ver
Usage
if not check_python_compatibility(17): print(f"WARNING: Python {sys.version} may not be compatible with Odoo 17")
Managing Multiple Venvs (Multi-Version Setup)
Separate venv per Odoo version
C:\TQ-WorkSpace\odoo\odoo14.venv\ # Python 3.8 C:\TQ-WorkSpace\odoo\odoo15.venv\ # Python 3.9 C:\TQ-WorkSpace\odoo\odoo17.venv\ # Python 3.11 C:\TQ-WorkSpace\odoo\odoo18.venv\ # Python 3.11
Each has its own requirements installed independently
Never share venvs across Odoo versions
- Server Startup (Local)
Basic Startup Commands
Basic startup — reads all config from .conf file
python -m odoo -c conf\myproject.conf
Explicit database override
python -m odoo -c conf\myproject.conf -d mydb
Development mode — enables auto-reload, full tracebacks, asset recompilation
python -m odoo -c conf\myproject.conf --dev=all
Development mode options (can combine):
--dev=reload — auto-reload Python on file change
--dev=qweb — detailed QWeb error reporting
--dev=werkzeug — Werkzeug debugger
--dev=xml — show XML errors in browser
--dev=all — enable all dev options (recommended)
With workers (production mode)
python -m odoo -c conf\myproject.conf --workers=4
Custom port
python -m odoo -c conf\myproject.conf --http-port=8070
Install module and stop
python -m odoo -c conf\myproject.conf -d mydb -i my_module --stop-after-init
Update module and stop
python -m odoo -c conf\myproject.conf -d mydb -u my_module --stop-after-init
Update ALL modules (slow — use only when necessary)
python -m odoo -c conf\myproject.conf -d mydb -u all --stop-after-init
Background Process (Windows)
Method 1: Start-Process (PowerShell)
Start-Process python -ArgumentList "-m odoo -c conf\myproject.conf" -WindowStyle Hidden
Method 2: Background job
$job = Start-Job -ScriptBlock { python -m odoo -c conf\myproject.conf } $job.Id # Save this to stop later
Method 3: With log file
Start-Process python -ArgumentList "-m odoo -c conf\myproject.conf" -WindowStyle Hidden -RedirectStandardOutput "logs\odoo.log" -RedirectStandardError "logs\odoo_err.log"
Method 4: Windows service (nssm recommended for production)
nssm install OdooService "python" "-m odoo -c C:\TQ-WorkSpace\odoo\odoo17\conf\myproject.conf" nssm start OdooService
Background Process (Linux/Mac)
Method 1: nohup (survives terminal close)
nohup python -m odoo -c conf/myproject.conf > logs/odoo.log 2>&1 & echo $! > logs/odoo.pid # Save PID for later
Method 2: screen session
screen -dmS odoo python -m odoo -c conf/myproject.conf
Method 3: tmux session
tmux new-session -d -s odoo "python -m odoo -c conf/myproject.conf"
Method 4: systemd service (production)
Create /etc/systemd/system/odoo17.service:
[Unit] Description=Odoo 17 Server After=network.target postgresql.service
[Service] Type=simple User=odoo WorkingDirectory=/opt/odoo17 ExecStart=/opt/odoo17/.venv/bin/python -m odoo -c conf/myproject.conf Restart=on-failure
[Install] WantedBy=multi-user.target
Enable and start
systemctl enable odoo17 systemctl start odoo17 systemctl status odoo17
Startup Sequence Verification
After starting, verify Odoo is running:
1. Check port is listening
netstat -ano | findstr :8069 # Windows lsof -i :8069 # Linux/Mac
2. Check HTTP response
curl -s -o /dev/null -w "%{http_code}" http://localhost:8069/web/login
Expected: 200
3. Check process
tasklist | findstr python # Windows ps aux | grep odoo # Linux/Mac
- Server Shutdown / Port Management
Windows: Kill by Port
Method 1: netstat + taskkill (CMD)
FOR /F "tokens=5" %P IN ('netstat -ano ^| findstr :8069') DO taskkill /PID %P /F
Method 2: PowerShell (more reliable)
$port = 8069 $process = Get-NetTCPConnection -LocalPort $port -ErrorAction SilentlyContinue if ($process) { Stop-Process -Id $process.OwningProcess -Force Write-Host "Killed process on port $port" } else { Write-Host "No process found on port $port" }
Method 3: Kill all Python processes (nuclear option)
taskkill /IM python.exe /F
Kill both Odoo ports (web + longpolling)
FOR /F "tokens=5" %P IN ('netstat -ano ^| findstr :8069') DO taskkill /PID %P /F FOR /F "tokens=5" %P IN ('netstat -ano ^| findstr :8072') DO taskkill /PID %P /F
Linux / macOS: Kill by Port
Method 1: lsof + kill
lsof -ti:8069 | xargs kill -9
Method 2: fuser
fuser -k 8069/tcp
Method 3: pkill for Odoo specifically
pkill -f "odoo.*8069"
Kill both ports
lsof -ti:8069 | xargs kill -9 lsof -ti:8072 | xargs kill -9
Verify port is free
lsof -i :8069 # Should return nothing
Find and Inspect Running Processes
Windows: Find Odoo-related Python processes
Get-Process python | Where-Object {$_.CommandLine -like "odoo"}
Windows: Detailed process with ports
netstat -ano | findstr :8069
Linux/Mac: Inspect what's on port 8069
lsof -i :8069 ss -tlnp | grep 8069
Graceful vs Force Stop
import subprocess import signal import os
def stop_graceful(pid: int, timeout: int = 10): """Try SIGTERM first, then SIGKILL after timeout.""" try: if os.name == 'nt': # Windows subprocess.run(['taskkill', '/PID', str(pid), '/F']) else: # Linux/Mac os.kill(pid, signal.SIGTERM) import time time.sleep(timeout) try: os.kill(pid, 0) # Check if still alive os.kill(pid, signal.SIGKILL) # Force kill except ProcessLookupError: pass # Already dead, good except Exception as e: print(f"Error stopping process {pid}: {e}")
- Configuration File Reference (.conf)
Complete Annotated Configuration
[options]
==========================================================
ADDON PATHS
==========================================================
Comma-separated list of directories containing Odoo modules
Order matters: modules found first take precedence
addons_path = odoo\addons,projects\myproject
==========================================================
SECURITY
==========================================================
Master password for the database manager UI
Set to a strong password in production
admin_passwd = CHANGE_ME_IN_PRODUCTION
==========================================================
DATABASE CONNECTION
==========================================================
db_host = localhost db_port = 5432 db_user = odoo db_password = odoo
Database name (optional — can be specified at startup with -d)
db_name = myproject17
Database filter (regex pattern — only show matching databases)
Multi-tenant: each project has its own prefix
dbfilter = myproject17.*
Max database connections per worker
db_maxconn = 64
==========================================================
NETWORK PORTS
==========================================================
Main web interface port
http_port = 8069
Longpolling port (chat, notifications, buses)
Called gevent_port in v17+, longpolling_port in v14-16
gevent_port = 8072
longpolling_port = 8072 # Use this for Odoo 14-16
Interface to bind (0.0.0.0 = all, 127.0.0.1 = localhost only)
http_interface = 0.0.0.0
==========================================================
WORKER PROCESSES
==========================================================
Number of worker processes
0 = single-threaded (development)
>0 = multi-worker (production, requires --workers flag)
Rule of thumb: 2 * CPU_CORES + 1
workers = 0
Cron workers (background tasks)
max_cron_threads = 2
==========================================================
MEMORY LIMITS (bytes)
==========================================================
Soft limit: worker restarts after processing current request
limit_memory_soft = 2147483648 # 2 GB
Hard limit: worker killed immediately
limit_memory_hard = 2684354560 # 2.5 GB
==========================================================
TIME LIMITS (seconds)
==========================================================
CPU time per request
limit_time_cpu = 600
Real (wall clock) time per request
limit_time_real = 1200
Time per cron task
limit_time_real_cron = 1800
==========================================================
PROXY / PRODUCTION
==========================================================
Set True when behind nginx/Apache reverse proxy
Required for correct IP forwarding and SSL headers
proxy_mode = False
==========================================================
LOGGING
==========================================================
Log output file (omit or leave empty for console output)
logfile = logs\odoo.log
Log level: debug, info, warning, error, critical
log_level = info
Log handler format
log_handler = :INFO
==========================================================
DATA DIRECTORY
==========================================================
Where Odoo stores file attachments, sessions, etc.
data_dir = data
==========================================================
EMAIL (optional)
==========================================================
smtp_server = localhost
smtp_port = 25
smtp_ssl = False
smtp_user = False
smtp_password = False
==========================================================
REPORT RENDERING
==========================================================
Path to wkhtmltopdf for PDF reports
wkhtmltopdf_command = wkhtmltopdf # Auto-detected if in PATH
Config Templates by Environment
--- DEVELOPMENT CONFIG ---
[options] addons_path = odoo\addons,projects\myproject admin_passwd = 123 db_host = localhost db_port = 5432 db_user = odoo db_password = odoo dbfilter = myproject17_dev http_port = 8069 gevent_port = 8072 workers = 0 log_level = debug
--- PRODUCTION CONFIG ---
[options] addons_path = odoo\addons,projects\myproject admin_passwd = STRONG_RANDOM_PASSWORD_HERE db_host = localhost db_port = 5432 db_user = odoo db_password = STRONG_DB_PASSWORD dbfilter = myproject17_prod http_port = 8069 gevent_port = 8072 workers = 4 limit_memory_soft = 2147483648 limit_memory_hard = 2684354560 limit_time_cpu = 600 limit_time_real = 1200 proxy_mode = True logfile = logs\odoo_prod.log log_level = warning data_dir = data_prod
- Module Operations
Install, Update, Scaffold
----------------------------------------
INSTALL (new module, first time)
----------------------------------------
python -m odoo -c conf\myproject.conf -d mydb -i my_module --stop-after-init
Install multiple modules
python -m odoo -c conf\myproject.conf -d mydb -i module1,module2,module3 --stop-after-init
----------------------------------------
UPDATE (after code changes — most common)
----------------------------------------
python -m odoo -c conf\myproject.conf -d mydb -u my_module --stop-after-init
Update multiple modules
python -m odoo -c conf\myproject.conf -d mydb -u module1,module2 --stop-after-init
Update ALL modules (slow, use sparingly)
python -m odoo -c conf\myproject.conf -d mydb -u all --stop-after-init
----------------------------------------
REFRESH MODULE LIST
----------------------------------------
Run before installing a NEW module that wasn't there before
python -m odoo -c conf\myproject.conf -d mydb --update-list
----------------------------------------
SCAFFOLD NEW MODULE
----------------------------------------
python odoo-bin scaffold my_new_module projects\myproject\
Creates: projects/myproject/my_new_module/ with standard structure
Module Management in Docker
Update module inside Docker container
docker-compose exec odoo python -m odoo
-c /etc/odoo/odoo.conf
-d mydb
-u my_module
--stop-after-init
Install module inside Docker container
docker-compose exec odoo python -m odoo
-c /etc/odoo/odoo.conf
-d mydb
-i my_module
--stop-after-init
Scaffold inside Docker
docker-compose exec odoo python /opt/odoo/source/odoo-bin scaffold my_module /opt/odoo/custom-addons/
Check Installed Modules
Via Odoo shell
python odoo-bin shell -d mydb
installed = self.env['ir.module.module'].search([('state', '=', 'installed')]) print(installed.mapped('name'))
Via psql directly
psql -U odoo -d mydb -c "SELECT name, state FROM ir_module_module WHERE state='installed' ORDER BY name;"
- Database Operations
Backup and Restore
----------------------------------------
BACKUP
----------------------------------------
SQL format (human-readable, larger file)
pg_dump -U odoo -h localhost mydb > backups/mydb_$(date +%Y%m%d_%H%M%S).sql
Custom format (compressed, faster restore)
pg_dump -U odoo -h localhost -Fc mydb > backups/mydb_$(date +%Y%m%d_%H%M%S).dump
Directory format (parallel dump — fastest for large DBs)
pg_dump -U odoo -h localhost -Fd mydb -j 4 -f backups/mydb_dir/
----------------------------------------
RESTORE
----------------------------------------
From SQL file
createdb -U odoo new_mydb psql -U odoo -d new_mydb < backups/mydb_20240101.sql
From custom dump
createdb -U odoo new_mydb pg_restore -U odoo -d new_mydb backups/mydb_20240101.dump
From directory format
createdb -U odoo new_mydb pg_restore -U odoo -d new_mydb -j 4 backups/mydb_dir/
----------------------------------------
MANAGE
----------------------------------------
Create database
createdb -U odoo newproject17
Drop database (DANGER: irreversible)
dropdb -U odoo oldproject
List all databases
psql -U odoo -l
Connect interactively
psql -U odoo -h localhost -d mydb
Reset Admin Password
Method 1: SQL UPDATE (fastest)
psql -U odoo -h localhost -d mydb -c
"UPDATE res_users SET password='$pbkdf2-sha512$...' WHERE login='admin';"
Better: Use Odoo's password hashing
psql -U odoo -h localhost -d mydb -c
"UPDATE res_users SET password='newpassword' WHERE login='admin';"
Note: Odoo hashes on next login attempt; for immediate hash use shell method
Method 2: Odoo shell (recommended)
python odoo-bin shell -d mydb
user = self.env['res.users'].search([('login', '=', 'admin')]) user.write({'password': 'newpassword'}) self.env.cr.commit()
Interactive Shell Usage
Start shell
python odoo-bin shell -d mydb
---- COMMON SHELL OPERATIONS ----
List all partners
env['res.partner'].search([], limit=10)
Find specific record
env['res.partner'].search([('name', 'like', 'TAQAT')])
Current user
env.user.name env.user.id
Raw SQL
env.cr.execute("SELECT id, name FROM res_partner LIMIT 5") env.cr.fetchall()
System parameters
env['ir.config_parameter'].sudo().get_param('web.base.url') env['ir.config_parameter'].sudo().set_param('web.base.url', 'https://mysite.com')
List installed modules
env['ir.module.module'].search([('state', '=', 'installed')]).mapped('name')
Commit changes (required for writes)
env.cr.commit()
- Docker Environment
Complete docker-compose.yml
version: '3.8'
services: db: image: postgres:15 container_name: odoo_db environment: POSTGRES_USER: ${POSTGRES_USER:-odoo} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-odoo} POSTGRES_DB: postgres volumes: - odoo_db_data:/var/lib/postgresql/data - ./backups:/backups ports: - "5432:5432" healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-odoo}"] interval: 10s timeout: 5s retries: 5 start_period: 20s restart: unless-stopped
odoo: build: context: . dockerfile: Dockerfile container_name: odoo_web depends_on: db: condition: service_healthy volumes: - .:/opt/odoo/source:ro - ./projects:/opt/odoo/custom-addons - ./conf:/etc/odoo:ro - odoo_filestore:/var/lib/odoo - ./logs:/var/log/odoo ports: - "${ODOO_HTTP_PORT:-8069}:8069" - "${ODOO_LONGPOLLING_PORT:-8072}:8072" environment: POSTGRES_HOST: db POSTGRES_PORT: 5432 POSTGRES_USER: ${POSTGRES_USER:-odoo} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-odoo} restart: unless-stopped command: odoo -c /etc/odoo/odoo.conf
volumes: odoo_db_data: odoo_filestore:
Docker Management Commands
---- BUILD ----
docker-compose build docker-compose build --no-cache # Force full rebuild
---- START / STOP ----
docker-compose up -d # Start in background docker-compose up # Start in foreground (see logs) docker-compose start # Start existing containers docker-compose stop # Stop containers (preserves state) docker-compose down # Stop and remove containers docker-compose down -v # Also remove volumes (DANGER: deletes data) docker-compose restart # Restart all docker-compose restart odoo # Restart only Odoo service
---- REBUILD AFTER CHANGES ----
docker-compose up -d --build # Rebuild and restart
---- LOGS ----
docker-compose logs -f # Follow all logs docker-compose logs -f odoo # Follow only Odoo logs docker-compose logs --tail=100 odoo
---- SHELL ACCESS ----
docker-compose exec odoo bash # Bash shell in Odoo container docker-compose exec db psql -U odoo # PostgreSQL in DB container
---- ODOO COMMANDS IN CONTAINER ----
docker-compose exec odoo python -m odoo -c /etc/odoo/odoo.conf -d mydb -u my_module --stop-after-init docker-compose exec odoo python -m odoo shell -d mydb -c /etc/odoo/odoo.conf
---- STATUS ----
docker-compose ps docker-compose top
---- DATABASE IN DOCKER ----
Backup
docker exec odoo_db pg_dump -U odoo mydb > backup.sql docker exec odoo_db pg_dump -U odoo -Fc mydb > backup.dump
Restore
cat backup.sql | docker exec -i odoo_db psql -U odoo mydb docker exec -i odoo_db pg_restore -U odoo -d mydb < backup.dump
- Dockerfile Reference (Per Odoo Version)
Odoo 14 Dockerfile
FROM python:3.8-slim-buster
ENV DEBIAN_FRONTEND=noninteractive ENV LANG=C.UTF-8
RUN apt-get update && apt-get install -y
build-essential
libpq-dev
libxml2-dev
libxslt1-dev
libjpeg62-turbo-dev
libfreetype6-dev
libsasl2-dev
libldap2-dev
libssl-dev
node-less
npm
git
postgresql-client
wget
curl
&& rm -rf /var/lib/apt/lists/*
Install wkhtmltopdf for PDF reports
RUN wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.buster_amd64.deb
&& dpkg -i wkhtmltox_0.12.6.1-3.buster_amd64.deb
&& rm wkhtmltox_0.12.6.1-3.buster_amd64.deb
WORKDIR /opt/odoo/source COPY requirements.txt . RUN pip install --upgrade pip && pip install -r requirements.txt
EXPOSE 8069 8072 CMD ["python", "-m", "odoo", "-c", "/etc/odoo/odoo.conf"]
Odoo 17 Dockerfile (Primary)
FROM python:3.10-slim-bookworm
ENV DEBIAN_FRONTEND=noninteractive ENV LANG=C.UTF-8
RUN apt-get update && apt-get install -y
build-essential
libpq-dev
libxml2-dev
libxslt1-dev
libjpeg-dev
libfreetype6-dev
libsasl2-dev
libldap2-dev
libssl-dev
libffi-dev
liblzma-dev
npm
git
postgresql-client
wget
curl
gettext-base
fonts-noto-cjk
fonts-noto-core
&& rm -rf /var/lib/apt/lists/*
Install wkhtmltopdf for PDF reports (Odoo 17 uses bookworm)
RUN wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
&& dpkg -i wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
&& rm wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
Install rtlcss for RTL/Arabic support
RUN npm install -g rtlcss
WORKDIR /opt/odoo/source COPY requirements.txt . RUN pip install --upgrade pip && pip install -r requirements.txt
Install GeoIP2 for geolocation features
RUN pip install geoip2
RUN mkdir -p /var/lib/odoo /var/log/odoo /etc/odoo EXPOSE 8069 8072 CMD ["python", "-m", "odoo", "-c", "/etc/odoo/odoo.conf"]
Odoo 18 Dockerfile
FROM python:3.11-slim-bookworm
ENV DEBIAN_FRONTEND=noninteractive ENV LANG=C.UTF-8
RUN apt-get update && apt-get install -y
build-essential
libpq-dev
libxml2-dev
libxslt1-dev
libjpeg-dev
libfreetype6-dev
libsasl2-dev
libldap2-dev
libssl-dev
libffi-dev
npm
git
postgresql-client
wget
curl
&& rm -rf /var/lib/apt/lists/*
RUN wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
&& dpkg -i wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
&& rm wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
RUN npm install -g rtlcss
WORKDIR /opt/odoo/source
COPY requirements.txt .
RUN pip install --upgrade pip
&& pip install -r requirements.txt
&& pip install cbor2
RUN mkdir -p /var/lib/odoo /var/log/odoo /etc/odoo EXPOSE 8069 8072 CMD ["python", "-m", "odoo", "-c", "/etc/odoo/odoo.conf"]
Odoo 19 Dockerfile
FROM python:3.12-slim-bookworm
ENV DEBIAN_FRONTEND=noninteractive ENV LANG=C.UTF-8
RUN apt-get update && apt-get install -y
build-essential
libpq-dev
libxml2-dev
libxslt1-dev
libjpeg-dev
libfreetype6-dev
libsasl2-dev
libldap2-dev
libssl-dev
libffi-dev
libmagic1
npm
git
postgresql-client
wget
curl
&& rm -rf /var/lib/apt/lists/*
RUN wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
&& dpkg -i wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
&& rm wkhtmltox_0.12.6.1-3.bookworm_amd64.deb
RUN npm install -g rtlcss
WORKDIR /opt/odoo/source
COPY requirements.txt .
RUN pip install --upgrade pip
&& pip install -r requirements.txt
&& pip install cbor2 python-magic
RUN mkdir -p /var/lib/odoo /var/log/odoo /etc/odoo EXPOSE 8069 8072 CMD ["python", "-m", "odoo", "-c", "/etc/odoo/odoo.conf"]
- PyCharm Configuration
Local Python Run Configuration (XML)
Save to .idea/runConfigurations/Odoo_[PROJECT].xml :
<component name="ProjectRunConfigurationManager"> <configuration default="false" name="Odoo 17 [PROJECT]" type="PythonConfigurationType" factoryName="Python"> <module name="[PROJECT_NAME]" /> <option name="INTERPRETER_OPTIONS" value="" /> <option name="PARENT_ENVS" value="true" /> <option name="SDK_HOME" value="$PROJECT_DIR$/.venv/Scripts/python.exe" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> <option name="IS_MODULE_SDK" value="false" /> <option name="ADD_CONTENT_ROOTS" value="true" /> <option name="ADD_SOURCE_ROOTS" value="true" /> <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" /> <option name="SCRIPT_NAME" value="$PROJECT_DIR$/odoo-bin" /> <option name="PARAMETERS" value="-c conf/[CONFIG].conf" /> <option name="SHOW_COMMAND_LINE" value="false" /> <option name="EMULATE_TERMINAL" value="true" /> <option name="MODULE_MODE" value="false" /> <option name="REDIRECT_INPUT" value="false" /> <option name="INPUT_FILE" value="" /> <method v="2" /> </configuration> </component>
Docker Compose Run Configuration (XML)
Save to .idea/runConfigurations/Odoo_Docker.xml :
<component name="ProjectRunConfigurationManager"> <configuration default="false" name="Odoo [VERSION] Docker" type="docker-deploy" factoryName="docker-compose.yml" server-name="Docker"> <deployment type="docker-compose.yml"> <settings> <option name="envFilePath" value="$PROJECT_DIR$/.env" /> <option name="composeFilePaths"> <list> <option value="$PROJECT_DIR$/docker-compose.yml" /> </list> </option> <option name="sourceFilePath" value="docker-compose.yml" /> </settings> </deployment> <method v="2" /> </configuration> </component>
PyCharm Settings (idea.properties / .idea/misc.xml)
<!-- .idea/misc.xml --> <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="Black"> <option name="sdkName" value="Python 3.11 (.venv)" /> </component> <component name="JavaScriptSettings"> <option name="languageLevel" value="ES6" /> </component> <component name="PythonProjectView"> <option name="showLibraryContents" value="false" /> </component> </project>
- VSCode Configuration
tasks.json (Local + Docker)
Save to .vscode/tasks.json :
{ "version": "2.0.0", "tasks": [ { "label": "Odoo: Start (dev mode)", "type": "shell", "command": "python -m odoo -c conf/${input:configFile} --dev=all", "group": {"kind": "build", "isDefault": true}, "presentation": { "reveal": "always", "panel": "new", "showReuseMessage": false }, "problemMatcher": [] }, { "label": "Odoo: Start (production mode)", "type": "shell", "command": "python -m odoo -c conf/${input:configFile} --workers=4", "group": "build", "presentation": {"reveal": "always", "panel": "new"} }, { "label": "Odoo: Stop (kill port 8069)", "type": "shell", "command": "FOR /F "tokens=5" %P IN ('netstat -ano ^| findstr :8069') DO taskkill /PID %P /F", "windows": { "command": "FOR /F "tokens=5" %P IN ('netstat -ano ^| findstr :8069') DO taskkill /PID %P /F" }, "linux": {"command": "lsof -ti:8069 | xargs kill -9"}, "osx": {"command": "lsof -ti:8069 | xargs kill -9"}, "presentation": {"reveal": "always"} }, { "label": "Odoo: Update Module", "type": "shell", "command": "python -m odoo -c conf/${input:configFile} -d ${input:database} -u ${input:moduleName} --stop-after-init", "presentation": {"reveal": "always", "panel": "shared"} }, { "label": "Odoo: Install Module", "type": "shell", "command": "python -m odoo -c conf/${input:configFile} -d ${input:database} -i ${input:moduleName} --stop-after-init", "presentation": {"reveal": "always"} }, { "label": "Docker: Start", "type": "shell", "command": "docker-compose up -d", "group": "build", "presentation": {"reveal": "always"} }, { "label": "Docker: Stop", "type": "shell", "command": "docker-compose down", "presentation": {"reveal": "always"} }, { "label": "Docker: Rebuild", "type": "shell", "command": "docker-compose up -d --build", "presentation": {"reveal": "always", "panel": "new"} }, { "label": "Docker: Logs", "type": "shell", "command": "docker-compose logs -f odoo", "presentation": {"reveal": "always", "panel": "shared"} }, { "label": "Docker: Shell", "type": "shell", "command": "docker-compose exec odoo bash", "presentation": {"reveal": "always", "panel": "new"} }, { "label": "Docker: Update Module", "type": "shell", "command": "docker-compose exec odoo python -m odoo -c /etc/odoo/odoo.conf -d ${input:database} -u ${input:moduleName} --stop-after-init", "presentation": {"reveal": "always"} } ], "inputs": [ { "id": "configFile", "type": "promptString", "description": "Config file name (e.g. myproject.conf)", "default": "odoo.conf" }, { "id": "database", "type": "promptString", "description": "Database name", "default": "myproject17" }, { "id": "moduleName", "type": "promptString", "description": "Module name(s) to install/update", "default": "my_module" } ] }
launch.json (Debugging)
Save to .vscode/launch.json :
{ "version": "0.2.0", "configurations": [ { "name": "Odoo: Debug (Local)", "type": "debugpy", "request": "launch", "module": "odoo", "args": ["-c", "conf/${input:configFile}", "--dev=all"], "cwd": "${workspaceFolder}", "python": "${workspaceFolder}/.venv/Scripts/python.exe", "justMyCode": false, "console": "integratedTerminal", "env": { "PYTHONDONTWRITEBYTECODE": "1" } }, { "name": "Odoo: Debug (Docker attach)", "type": "debugpy", "request": "attach", "connect": { "host": "localhost", "port": 5678 }, "pathMappings": [ { "localRoot": "${workspaceFolder}", "remoteRoot": "/opt/odoo/source" } ] }, { "name": "Odoo: Debug with DB override", "type": "debugpy", "request": "launch", "module": "odoo", "args": [ "-c", "conf/${input:configFile}", "-d", "${input:database}", "--dev=all" ], "cwd": "${workspaceFolder}", "python": "${workspaceFolder}/.venv/Scripts/python.exe", "justMyCode": false, "console": "integratedTerminal" } ], "inputs": [ { "id": "configFile", "type": "promptString", "description": "Config file name", "default": "odoo.conf" }, { "id": "database", "type": "promptString", "description": "Database name", "default": "myproject17" } ] }
settings.json (Workspace)
Save to .vscode/settings.json :
{ "python.analysis.extraPaths": [ "./odoo", "./odoo/addons", "./projects" ], "python.languageServer": "Pylance", "python.defaultInterpreterPath": ".venv/Scripts/python.exe", "python.analysis.typeCheckingMode": "basic", "python.analysis.autoImportCompletions": true,
"files.exclude": { "/pycache": true, "/.pyc": true, "**/.pyo": true, "odoo.egg-info": true }, "search.exclude": { "odoo.egg-info": true, "/pycache": true, ".venv": true, "/.pyc": true }, "editor.rulers": [79, 120], "editor.formatOnSave": false, "files.associations": { ".xml": "xml", ".conf": "ini", ".po": "po" }, "xml.validation.enabled": false, "editor.tabSize": 4, "editor.insertSpaces": true, "[python]": { "editor.tabSize": 4 }, "[xml]": { "editor.tabSize": 4 } }
- Multi-Project / Multi-Tenant Setup
Port Allocation Strategy
Project A — Port 8069 (default)
http_port = 8069 gevent_port = 8072 dbfilter = projecta17.*
Project B — Port 8070
http_port = 8070 gevent_port = 8073 dbfilter = projectb17.*
Project C — Port 8071
http_port = 8071 gevent_port = 8074 dbfilter = projectc17.*
Nginx Reverse Proxy (Domain Routing)
/etc/nginx/sites-available/projecta.conf
server { listen 80; server_name projecta.example.com;
location / {
proxy_pass http://127.0.0.1:8069;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /longpolling {
proxy_pass http://127.0.0.1:8072;
proxy_set_header Host $host;
}
}
/etc/nginx/sites-available/projectb.conf
server { listen 80; server_name projectb.example.com;
location / {
proxy_pass http://127.0.0.1:8070;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /longpolling {
proxy_pass http://127.0.0.1:8073;
proxy_set_header Host $host;
}
}
Running Multiple Instances Simultaneously
Terminal 1 — Project A
..venv\Scripts\activate python -m odoo -c conf\projecta17.conf
Terminal 2 — Project B
..venv\Scripts\activate python -m odoo -c conf\projectb17.conf
Background both (Windows PowerShell)
Start-Process python -ArgumentList "-m odoo -c conf\projecta17.conf" -WindowStyle Hidden Start-Process python -ArgumentList "-m odoo -c conf\projectb17.conf" -WindowStyle Hidden
- Production Checklist
Before going live, verify each item:
SECURITY [ ] admin_passwd set to strong, unique password (not '123' or 'admin') [ ] db_password set to strong password [ ] PostgreSQL user has limited privileges (not superuser) [ ] Firewall: only ports 80/443 exposed to internet [ ] SSH key-based authentication (no password login) [ ] SSL/TLS certificate installed (Let's Encrypt via certbot)
SERVER CONFIGURATION [ ] proxy_mode = True (if behind nginx/Apache) [ ] workers = 4 (or more based on CPU: 2*CPU+1) [ ] max_cron_threads = 2 (or 1 for smaller servers) [ ] limit_memory_soft / limit_memory_hard configured [ ] limit_time_cpu / limit_time_real configured [ ] logfile set and log rotation configured [ ] data_dir set and backed up
DATABASE [ ] dbfilter set to restrict accessible databases [ ] Automated backup schedule configured [ ] Backup tested (actually restore to verify) [ ] PostgreSQL pg_hba.conf restricts remote access
INFRASTRUCTURE [ ] Regular server snapshots configured [ ] Monitoring configured (uptime, CPU, memory) [ ] Alert on server down / high memory [ ] wkhtmltopdf installed for PDF reports [ ] rtlcss installed if using Arabic/RTL
ODOO SPECIFIC [ ] website.base.url system parameter set correctly [ ] Email (SMTP) configured and tested [ ] CDN configured if using attachments [ ] Session timeout configured appropriately
- Troubleshooting Guide
Common Error Reference
Problem Likely Cause Solution
Address already in use: 8069
Previous Odoo still running lsof -ti:8069 | xargs kill -9 or Windows: find/kill PID
could not connect to server: Connection refused
PostgreSQL not running Start PostgreSQL: net start postgresql (Win) or systemctl start postgresql
ImportError: No module named X
Missing Python package pip install X or pip install -r requirements.txt
Module not appearing in apps list Module path not in addons_path Add path to .conf, run --update-list
Access Denied on module install Wrong admin_passwd Check admin_passwd in .conf matches what you typed
Docker health check failing PostgreSQL container not ready Increase start_period in healthcheck config
wkhtmltopdf not found (PDF fails) wkhtmltopdf not installed/PATH Install via apt/choco, add to PATH
Longpolling not working Port 8072 blocked or workers=0
Set workers > 0 for longpolling
Dev mode not auto-reloading Only using --dev=reload
Use --dev=all for full dev mode
database "X" does not exist
Database not created yet createdb -U odoo X
FATAL: role "odoo" does not exist
PostgreSQL user not created createuser -s odoo
Changes not reflecting after update Browser cache Hard refresh: Ctrl+Shift+R
XML view error (blank page) Malformed XML Check XML syntax, look at server logs
KeyError: 'field_name' in view Field not in model Check field exists in Python model
Slow startup Too many modules Check -i / -u flags, use --stop-after-init
Diagnostic Commands
Check PostgreSQL status
pg_isready -h localhost -p 5432
Check Odoo port
netstat -ano | findstr :8069 # Windows lsof -i :8069 # Linux/Mac
Check Odoo logs (last 50 lines)
Get-Content logs\odoo.log -Tail 50 # PowerShell tail -50 logs/odoo.log # Linux/Mac
Check Python version
python --version
Check installed packages
pip list | findstr odoo # Windows pip list | grep odoo # Linux/Mac
Test PostgreSQL connection
psql -U odoo -h localhost -c "SELECT 1"
Docker diagnostics
docker-compose ps docker-compose logs --tail=50 odoo docker inspect odoo_web | grep -A 5 "Health"
- Interactive Shell Usage
Starting the Shell
Local
python odoo-bin shell -d mydatabase
Docker
docker-compose exec odoo python -m odoo shell -d mydatabase -c /etc/odoo/odoo.conf
With specific config
python odoo-bin shell -d mydatabase -c conf/myproject.conf
Common Shell Operations
---- PARTNERS ----
List all partners
env['res.partner'].search([], limit=10)
Search by name
env['res.partner'].search([('name', 'ilike', 'company')])
Create partner
p = env['res.partner'].create({'name': 'Test Company', 'is_company': True}) env.cr.commit()
---- USERS ----
env.user.name # Current user name env.user.id # Current user ID env.user.groups_id # User's groups
Find admin user
admin = env['res.users'].browse(2) admin.login
---- MODULES ----
List installed modules
env['ir.module.module'].search([('state', '=', 'installed')]).mapped('name')
Find specific module
env['ir.module.module'].search([('name', '=', 'sale')])[0].state
---- SYSTEM PARAMS ----
env['ir.config_parameter'].sudo().get_param('web.base.url') env['ir.config_parameter'].sudo().set_param('web.base.url', 'https://mysite.com') env.cr.commit()
---- RAW SQL ----
env.cr.execute("SELECT id, name, state FROM ir_module_module WHERE state='installed' LIMIT 10") rows = env.cr.fetchall() for row in rows: print(row)
---- COMMIT CHANGES ----
env.cr.commit() # Always commit after writes!
---- ROLLBACK ----
env.cr.rollback() # Undo uncommitted changes
- Environment Variables (.env)
Docker .env File
.env (for docker-compose)
Copy from .env.example and customize
PostgreSQL settings
POSTGRES_USER=odoo POSTGRES_PASSWORD=change_me_in_production POSTGRES_DB=postgres
Odoo ports
ODOO_HTTP_PORT=8069 ODOO_LONGPOLLING_PORT=8072
Development mode (set to 'all' for dev, empty for prod)
DEV_MODE=all
Build settings
ODOO_VERSION=17
Optional: custom config file
ODOO_CONFIG=/etc/odoo/odoo.conf
Local Development .env File
.env.local (for local dev, load with dotenv or manually)
ODOO_CONFIG=conf/myproject17.conf ODOO_DATABASE=myproject17 ODOO_PORT=8069 ODOO_LONGPOLLING_PORT=8072 PGUSER=odoo PGPASSWORD=odoo PGHOST=localhost PGPORT=5432
Using .env in PowerShell
Load .env file in PowerShell
Get-Content .env | ForEach-Object { if ($_ -match '^([^#][^=])=(.)$') { [System.Environment]::SetEnvironmentVariable($matches[1].Trim(), $matches[2].Trim()) } }
Or use dotenv package
pip install python-dotenv
- Available Commands Reference
Command Overview Table
Command Description Environment
/odoo-service
Main help and status Any
/odoo-start
Start Odoo server (auto-detects env) Local / Docker
/odoo-stop
Stop Odoo server / containers Local / Docker
/odoo-init
Initialize new Odoo environment Local / Docker
/odoo-db
Database operations (backup, restore, create, drop) Local / Docker
/odoo-docker
Docker management (build, up, down, logs, shell) Docker
/odoo-ide
Generate PyCharm/VSCode configurations Any
Quick Start Examples
--- LOCAL VENV WORKFLOW ---
1. Initialize new project
/odoo-init --version 17 --project myproject --port 8069
2. Start in dev mode
/odoo-start --config myproject17.conf --dev
3. Update a module
python -m odoo -c conf/myproject17.conf -d myproject17 -u my_module --stop-after-init
4. Stop server
/odoo-stop
5. Backup database
/odoo-db backup --db myproject17
--- DOCKER WORKFLOW ---
1. Initialize Docker project
/odoo-init --docker --version 17 --project myproject
2. Build and start
/odoo-docker up
3. View logs
/odoo-docker logs
4. Open shell
/odoo-docker shell
5. Update module
/odoo-docker update --db mydb --module my_module
6. Backup database
/odoo-db backup --docker odoo_db --db mydb
7. Stop
/odoo-docker down
- Odoo Version-Specific Notes
Odoo 14 Specifics
Use longpolling_port (not gevent_port)
longpolling_port = 8072
Python must be 3.7-3.10 (3.8 recommended)
node.js + npm required for JS assets
npm install -g less less-plugin-clean-css
Worker restart is via SIGHUP on Linux
kill -HUP $(cat logs/odoo.pid)
Odoo 17 Specifics
gevent_port is the correct name (not longpolling_port)
gevent_port = 8072
rtlcss required for Arabic/RTL language support
npm install -g rtlcss
Assets are compiled differently (OWL 2.x)
Use --dev=all for asset recompilation
New: ORM caching improvements
New: WhatsApp integration support
Odoo 18-19 Specifics
cbor2 package required
pip install cbor2
Odoo 19: libmagic1 required
apt-get install libmagic1 pip install python-magic
New REST API framework in v17+
New: spreadsheet integration
PostgreSQL 15+ recommended for v18/19
- Script Usage Reference
server_manager.py
Start Odoo
python odoo-service/scripts/server_manager.py start --config conf/myproject.conf --dev
Stop Odoo
python odoo-service/scripts/server_manager.py stop --port 8069
Check status
python odoo-service/scripts/server_manager.py status
Restart
python odoo-service/scripts/server_manager.py restart --config conf/myproject.conf
Install module
python odoo-service/scripts/server_manager.py install --config conf/myproject.conf --db mydb --module my_module
Update module
python odoo-service/scripts/server_manager.py update --config conf/myproject.conf --db mydb --module my_module
env_initializer.py
Initialize complete environment
python odoo-service/scripts/env_initializer.py init --version 17 --project myproject --port 8069
Just create venv
python odoo-service/scripts/env_initializer.py venv --python python3.11
Check environment
python odoo-service/scripts/env_initializer.py check
Generate config file only
python odoo-service/scripts/env_initializer.py conf --project myproject --version 17 --db mydb
db_manager.py
Backup database
python odoo-service/scripts/db_manager.py backup --db myproject17 --output backups/
Restore from dump
python odoo-service/scripts/db_manager.py restore --file backups/backup.dump --db restored_db
Create database
python odoo-service/scripts/db_manager.py create --db newproject17
Drop database
python odoo-service/scripts/db_manager.py drop --db oldproject
List databases
python odoo-service/scripts/db_manager.py list
Reset admin password
python odoo-service/scripts/db_manager.py reset-admin --db myproject17 --password newpass
docker_manager.py
Generate Docker files
python odoo-service/scripts/docker_manager.py init --version 17 --project myproject
Build image
python odoo-service/scripts/docker_manager.py build
Start containers
python odoo-service/scripts/docker_manager.py up
Stop containers
python odoo-service/scripts/docker_manager.py down
Follow logs
python odoo-service/scripts/docker_manager.py logs
Open shell
python odoo-service/scripts/docker_manager.py shell
Check status
python odoo-service/scripts/docker_manager.py status
ide_configurator.py
Generate VSCode config (local env)
python odoo-service/scripts/ide_configurator.py --ide vscode --env local --config myproject.conf
Generate PyCharm config (Docker)
python odoo-service/scripts/ide_configurator.py --ide pycharm --env docker --project myproject
Generate both IDE configs
python odoo-service/scripts/ide_configurator.py --ide both --env docker --project myproject
Generate .gitignore
python odoo-service/scripts/ide_configurator.py --gitignore-only
This skill covers Odoo 14-19 server lifecycle management across all environments. For project-specific patterns, refer to version-specific CLAUDE.md files.