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.
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 -vRestart if requested. Launch Ubuntu, create <linux-user>, then verify:
id -un
printf '%s\n' "$HOME"
uname -aThe 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-originConfigure 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 || truecommand -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.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 yeschmod 600 ~/.ssh/config
ssh-add ~/.ssh/id_ed25519_work
ssh -T git@github-workConfirm 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 ..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 --short6. 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 rtlcssRead 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 --versionThe 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_devIf 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)
PYReview 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 = Falsechmod 600 config/odoo.conf
printf '%s\n' 'config/odoo.conf' 'logs/' '*.dump' >> .gitignoredb_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-initLater update
./.venv/bin/python ./odoo/odoo-bin \
-c ./config/odoo.conf -d odoo18_dev \
-u your_custom_module --stop-after-initA 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_devOpen http://localhost:8069 in Windows, then verify from Ubuntu:
curl -I http://127.0.0.1:8069/web/loginDebug
./.venv/bin/python ./odoo/odoo-bin \
-c ./config/odoo.conf -d odoo18_dev \
--dev=all --log-level=debugConfigure 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_testConfirm 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-onlyRefresh 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
| Symptom | Likely cause | Safe check or fix |
|---|---|---|
git is not /usr/bin/git | A Windows executable or shell alias takes priority. | Run type -a git; call /usr/bin/git; remove the conflicting WSL alias. |
| SSH uses the wrong account | The default host selects another key. | Use the github-work remote and run ssh -vT git@github-work. Inspect filenames privately. |
| Access denied after key registration | Organization SSO is not approved. | Authorize the public key in organization settings, then retry the alias. |
python3.12 unavailable | Ubuntu package sets differ. | Check the release and use a trusted isolated Python installer. Do not replace system Python. |
| Odoo import fails | Wrong interpreter or working directory. | Run from odoo/ with ../.venv/bin/python. |
| Peer authentication fails | Linux 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 found | An 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 pull | The 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 8069 | Odoo 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 wrong | wkhtmltopdf is unpatched or the wrong release. | Verify output includes 0.12.6.1 (with patched qt). |
| Project permission denied | Known 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 = Falseonly 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_workRecreate 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-workselects 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.confis mode 600, untracked, and has a generated secret.your_custom_moduleinstalls with-iand updates with-u.- Tests pass in a disposable database.
http://localhost:8069opens 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: