Home Blog Odoo 18 on WSL2

Odoo Engineering

Set Up Odoo 18 Community on Windows with WSL2

A Linux-native, identity-separated development environment with PostgreSQL, Python 3.12, private add-ons, debugging, tests and safe local operations.

Windows workstation connected to WSL2, Python, Odoo modules and PostgreSQL
Table of contents
  1. Architecture and trust boundaries
  2. Install and verify WSL2
  3. Separate Git identities
  4. Dedicated work SSH identity
  5. Project and source clones
  6. Linux packages and PDF support
  7. Python 3.12 environment
  8. PostgreSQL role and database
  9. Detect add-ons roots
  10. Safe Odoo configuration
  11. Install and update your_custom_module
  12. Start, debug, and test
  13. Pull safely
  14. Troubleshooting
  15. Security practices
  16. Local-only backup and restore
  17. Cleanup
  18. Final checklist

Public and sanitized: every organization, repository, module, branch and email value is a generic placeholder. Replace placeholders locally and never publish credentials, database dumps, private remotes or key material.

1. Architecture and trust boundaries

WSL2 supplies a real Linux kernel while Windows remains the desktop host. Put source, the virtual environment, logs, and database data in Linux storage for predictable permissions and file performance.

Odoo development architectureWindows host with personal Git and browser connects through localhost forwarding to Odoo in WSL2. WSL uses a separate Git and SSH identity. Odoo uses a Python virtual environment and local PostgreSQL.WINDOWS HOSTPersonal Windows Gitpersonal identity + authWindows browserlocalhost:8069WSL2 UBUNTU/usr/bin/gitwork identity · SSH aliasOdoo 18 CommunityPython 3.12 · project .venvPostgreSQLpeer auth · non-superuserGit hostgithub-work aliaslocalhost forwardingseparate boundary

Linux-native project

Use ~/projects/odoo18-dev, not /mnt/c. Run Odoo as the normal Linux user.

Local exposure only

Bind to 127.0.0.1. Windows reaches Odoo through WSL localhost forwarding.

2. Install and verify WSL2

In elevated Windows PowerShell:

wsl --install -d Ubuntu
wsl --update
wsl --set-default-version 2
wsl -l -v

Restart if requested. Launch Ubuntu, create <linux-user>, then verify:

id -un
printf '%s\n' "$HOME"
uname -a

The distribution must report version 2. Keep the project in Linux storage.

3. Keep Windows and WSL Git identities separate

Configure personal identity with Windows Git in PowerShell:

git config --global user.name "Your Personal Name"
git config --global user.email "[email protected]"
git config --global --list --show-origin

Configure work identity with Linux Git in Ubuntu:

command -v git
/usr/bin/git --version
/usr/bin/git config --global user.name "Your Work Name"
/usr/bin/git config --global user.email "<WORK_EMAIL>"
/usr/bin/git config --global --list --show-origin
/usr/bin/git config --show-origin --get-all credential.helper || true

command -v git must return /usr/bin/git. Do not connect WSL Git to a Windows credential helper when strict separation is required. Repository-local name and email settings offer the narrowest scope.

4. Create a dedicated work SSH identity

Create a passphrase-protected key in Ubuntu:

install -d -m 700 ~/.ssh
ssh-keygen -t ed25519 -C "<WORK_EMAIL>" -f ~/.ssh/id_ed25519_work
chmod 600 ~/.ssh/id_ed25519_work
chmod 644 ~/.ssh/id_ed25519_work.pub
Private means private. Register only the public .pub file. Never publish key text, fingerprints, or verbose SSH traces.

Preserve existing SSH settings and add:

Host github-work
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_work
    IdentitiesOnly yes
    AddKeysToAgent yes
chmod 600 ~/.ssh/config
ssh-add ~/.ssh/id_ed25519_work
ssh -T git@github-work

Confirm the intended work account. If organization SSO is enforced, authorize this public key in the Git host settings. Login, MFA, key registration, and SSO approval are manual trust steps.

5. Create the project and clone sources

mkdir -p ~/projects/odoo18-dev/{config,custom-addons,docs,logs,scripts}
cd ~/projects/odoo18-dev
/usr/bin/git clone --branch 18.0 --single-branch \
  https://github.com/odoo/odoo.git odoo
/usr/bin/git clone git@github-work:YOUR_ORG/YOUR_WORK_REPO.git work-repo
cd work-repo
/usr/bin/git switch '<WORK_BRANCH>'
/usr/bin/git config user.name "Your Work Name"
/usr/bin/git config user.email "<WORK_EMAIL>"
cd ..
~/projects/odoo18-dev/ ├── .venv/ # created later ├── work-repo/ │ └── addons/ # example detected root ├── config/ │ └── odoo.conf ├── custom-addons/ ├── docs/ ├── logs/ ├── odoo/ │ ├── addons/ │ ├── odoo/addons/ │ └── odoo-bin └── scripts/

Verify both checkouts:

/usr/bin/git -C odoo branch --show-current
/usr/bin/git -C odoo rev-parse --short HEAD
/usr/bin/git -C work-repo branch --show-current
/usr/bin/git -C work-repo status --short

6. Install Linux packages and PDF support

sudo apt update
sudo apt install -y \
  build-essential git curl ca-certificates pkg-config \
  python3.12 python3.12-venv python3.12-dev \
  libldap2-dev libsasl2-dev libssl-dev libpq-dev \
  libxml2-dev libxslt1-dev libjpeg-dev zlib1g-dev libffi-dev \
  nodejs npm postgresql postgresql-client
sudo npm install -g rtlcss

Read odoo/requirements.txt. Let APT select supported package replacements; never force broken dependencies.

PDF reports

Use wkhtmltopdf 0.12.6.1 with patched Qt from a trusted project release package matching Ubuntu and the CPU architecture. Inspect the downloaded package before installing it:

dpkg-deb -f ./wkhtmltox_0.12.6.1-*.deb Package Version Architecture Depends
sudo apt install ./wkhtmltox_0.12.6.1-*.deb
wkhtmltopdf --version
wkhtmltoimage --version

The output should include 0.12.6.1 (with patched qt). Do not use --force-depends.

7. Create the Python 3.12 environment

cd ~/projects/odoo18-dev
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip wheel setuptools
python -m pip install -r odoo/requirements.txt
./.venv/bin/python --version
cd odoo
../.venv/bin/python -c "import odoo; print(odoo.release.version)"
cd ..

Run the import from the source checkout. Do not install project packages into Ubuntu's system Python.

8. Create a non-superuser PostgreSQL role and database

A role matching the Linux login enables passwordless local peer authentication:

sudo service postgresql start
sudo -u postgres createuser --no-superuser --createdb --no-createrole "$USER"
sudo -u postgres createdb --owner="$USER" odoo18_dev

If either object exists, inspect it rather than deleting it:

sudo -u postgres psql -tAc \
  "SELECT rolname, rolsuper, rolcreatedb, rolcreaterole FROM pg_roles WHERE rolname = '$USER';"
sudo -u postgres psql -tAc \
  "SELECT datname, pg_get_userbyid(datdba) FROM pg_database WHERE datname = 'odoo18_dev';"
psql -d odoo18_dev -tAc "SELECT current_user, current_database();"

The role must not be a superuser; the database owner should be <linux-user>.

9. Detect add-ons roots

An add-ons root directly contains Odoo module folders. Detect roots without importing repository code:

cd ~/projects/odoo18-dev
python3 - <<'PY'
from pathlib import Path
for repo in (Path('odoo'), Path('work-repo'), Path('custom-addons')):
    roots = {m.parent.parent.resolve() for m in repo.rglob('__manifest__.py')}
    for root in sorted(roots):
        print(root)
PY

Review documentation and manifests. Confirm every dependency of your_custom_module exists in a detected root. This guide uses the generic example ~/projects/odoo18-dev/work-repo/addons. Do not publish private names or dependency details.

10. Write a safe Odoo configuration

Create config/odoo.conf. Replace <linux-user> and generate a unique local master password; never use the literal placeholder in a running setup.

[options]
admin_passwd = <GENERATE_A_LOCAL_SECRET>
db_host = False
db_port = False
db_user = <linux-user>
db_password = False
db_name = odoo18_dev
addons_path = /home/<linux-user>/projects/odoo18-dev/odoo/addons,/home/<linux-user>/projects/odoo18-dev/odoo/odoo/addons,/home/<linux-user>/projects/odoo18-dev/work-repo/addons,/home/<linux-user>/projects/odoo18-dev/custom-addons
http_interface = 127.0.0.1
http_port = 8069
logfile = /home/<linux-user>/projects/odoo18-dev/logs/odoo.log
log_level = info
proxy_mode = False
list_db = False
chmod 600 config/odoo.conf
printf '%s\n' 'config/odoo.conf' 'logs/' '*.dump' >> .gitignore

db_password = False is for local Unix-socket peer authentication. admin_passwd protects database management operations and is not a PostgreSQL password.

11. Install and update your_custom_module

Initial install

cd ~/projects/odoo18-dev
./.venv/bin/python ./odoo/odoo-bin \
  -c ./config/odoo.conf -d odoo18_dev \
  -i your_custom_module --stop-after-init

Later update

./.venv/bin/python ./odoo/odoo-bin \
  -c ./config/odoo.conf -d odoo18_dev \
  -u your_custom_module --stop-after-init

A source pull does not migrate the database. Read the complete log, confirm the registry loaded, and verify state:

psql -d odoo18_dev -tAc \
  "SELECT name, state FROM ir_module_module WHERE name = 'your_custom_module';"

12. Start, debug, and test

Start

cd ~/projects/odoo18-dev
./.venv/bin/python ./odoo/odoo-bin -c ./config/odoo.conf -d odoo18_dev

Open http://localhost:8069 in Windows, then verify from Ubuntu:

curl -I http://127.0.0.1:8069/web/login

Debug

./.venv/bin/python ./odoo/odoo-bin \
  -c ./config/odoo.conf -d odoo18_dev \
  --dev=all --log-level=debug

Configure the editor to launch ./odoo/odoo-bin with .venv/bin/python, working directory ~/projects/odoo18-dev, and arguments -c config/odoo.conf -d odoo18_dev --dev=all. Keep secrets out of editor settings.

Test in a disposable database

createdb odoo18_test
./.venv/bin/python ./odoo/odoo-bin \
  -c ./config/odoo.conf -d odoo18_test \
  -i your_custom_module --test-enable --test-tags /your_custom_module \
  --stop-after-init
dropdb odoo18_test

Confirm the disposable database name before dropping it.

13. Pull changes safely

cd ~/projects/odoo18-dev
/usr/bin/git -C odoo status --short
/usr/bin/git -C work-repo status --short
/usr/bin/git -C odoo fetch --prune
/usr/bin/git -C work-repo fetch --prune
/usr/bin/git -C work-repo branch --show-current
/usr/bin/git -C work-repo log --oneline --decorate HEAD..@{upstream}

When both worktrees are clean and the selected branches are correct:

/usr/bin/git -C odoo pull --ff-only
/usr/bin/git -C work-repo pull --ff-only

Refresh changed Python requirements, run -u your_custom_module --stop-after-init, execute tests, and make a real HTTP request. Create a local backup before a risky migration.

14. Troubleshooting

SymptomLikely causeSafe check or fix
git is not /usr/bin/gitA Windows executable or shell alias takes priority.Run type -a git; call /usr/bin/git; remove the conflicting WSL alias.
SSH uses the wrong accountThe default host selects another key.Use the github-work remote and run ssh -vT git@github-work. Inspect filenames privately.
Access denied after key registrationOrganization SSO is not approved.Authorize the public key in organization settings, then retry the alias.
python3.12 unavailableUbuntu package sets differ.Check the release and use a trusted isolated Python installer. Do not replace system Python.
Odoo import failsWrong interpreter or working directory.Run from odoo/ with ../.venv/bin/python.
Peer authentication failsLinux login and PostgreSQL role differ.Compare id -un and role output; inspect pg_hba.conf. Do not add a plaintext password as a shortcut.
your_custom_module is not foundAn add-ons root is absent or points at the wrong level.Re-run manifest discovery. Add the direct parent of the module folder.
Registry fails after a pullThe database needs an update or a dependency is absent.Stop Odoo, inspect the full log privately, verify dependencies, then use -u your_custom_module.
Windows cannot open port 8069Odoo stopped, binding is wrong, or forwarding failed.Check WSL curl and ss -ltnp; retain 127.0.0.1; restart WSL only if needed.
PDF layout is wrongwkhtmltopdf is unpatched or the wrong release.Verify output includes 0.12.6.1 (with patched qt).
Project permission deniedKnown project files were created as root.Inspect ownership and repair only known project paths. Never run Odoo with sudo.

15. Security practices

  • Register only the public SSH key; protect the private key with a passphrase.
  • Do not publish fingerprints, verbose SSH traces, private remotes, or organization details.
  • Keep odoo.conf, logs, dumps, and editor secrets untracked.
  • Generate a unique admin_passwd; never reuse an account password.
  • Use db_password = False only for local peer authentication over a Unix socket.
  • Bind to 127.0.0.1. A shared deployment needs a reviewed reverse proxy and transport security.
  • Run Odoo and Git as <linux-user>, never as root.
  • Read manifests and scripts before execution; do not import arbitrary repository code for discovery.
  • Apply Ubuntu and PostgreSQL security updates regularly.
  • Treat backups as sensitive because they may contain personal or business data.

16. Local-only backup and restore

Stop Odoo or ensure a consistent state. Create a protected custom-format dump:

cd ~/projects/odoo18-dev
install -d -m 700 backups
pg_dump --format=custom --file=backups/odoo18_dev.dump odoo18_dev
chmod 600 backups/odoo18_dev.dump
pg_restore --list backups/odoo18_dev.dump | sed -n '1,20p'

Do not commit, email, or upload the dump. Restore into a new local database:

createdb odoo18_restore
pg_restore --no-owner --role="$USER" \
  --dbname=odoo18_restore backups/odoo18_dev.dump
psql -d odoo18_restore -tAc "SELECT current_database(), current_user;"

Back up any file-backed attachment store consistently with the database. A database-only restore may be incomplete.

17. Cleanup

Stop Odoo with Ctrl+C. Remove only disposable artifacts after checking names and paths:

dropdb --if-exists odoo18_test
rm -rf ~/projects/odoo18-dev/.venv
ssh-add -d ~/.ssh/id_ed25519_work

Recreate the environment from odoo/requirements.txt. Verify a restored database name before using dropdb. Never delete the main database or project tree during routine cleanup.

18. Final checklist

  • Ubuntu runs under WSL version 2.
  • The project is under ~/projects, not /mnt/c.
  • WSL Git resolves to /usr/bin/git.
  • Windows personal and WSL work Git identities are separate.
  • github-work selects only ~/.ssh/id_ed25519_work.
  • The public key is registered and organization SSO is authorized.
  • Official Odoo Community source is on branch 18.0.
  • The work repository is on approved <WORK_BRANCH>.
  • Python 3.12 runs from the project .venv.
  • wkhtmltopdf reports 0.12.6.1 with patched Qt.
  • PostgreSQL uses a matching non-superuser role and peer authentication.
  • Every add-ons path is a detected module-container root.
  • odoo.conf is mode 600, untracked, and has a generated secret.
  • your_custom_module installs with -i and updates with -u.
  • Tests pass in a disposable database.
  • http://localhost:8069 opens from Windows.
  • Dumps, logs, credentials, and private architecture remain local.

Official references

This guide was reviewed against the current vendor documentation on 24 July 2026:

Back to all guides
Call us Chat on WhatsApp