- Dockerfile: verbatim upstream odoo/docker 19.0 + pip install requirements.txt - easypanel.schema.json: source.type=dockerfile with inline Dockerfile - deploy.py: calls Easypanel tRPC API directly (bypass MCP that only supports image) - .gitea/workflows/build.yml: optional Gitea Actions CI - README.md: full research + reproduction steps
6.0 KiB
stateless-container-for-onebspace-development
Stateless hot-reload Odoo 19 development workspace for the
1b-agent/os-dev repo, deployed on the Easypanel project
onebspace.
This repo is the blueprint — the live state is on the
Easypanel onebspace project
(odoo-dev + odoo-dev-db services) and the bind-mounted workspace
at /root/Documents/trae_projects/one_b_os/os-dev/.
1. What this gives you
- No image rebuilds for code edits. Edit any
.pyinos-dev/odoo/addons/...and--dev=reload(watchdog) restarts Odoo in milliseconds. - Full hot-reload flags baked into
odoo.conf:dev_mode = reload,qweb,assets,xml,werkzeugreload→ restart on.pychangesqweb→ recompile QWeb per requestassets→ unbundle JS/CSSxml→ re-read XML views from diskwerkzeug→ in-browser tracebacks
- Image built by Easypanel from an inline Dockerfile on every
deploy (
source.type=dockerfile), no Gitea Container Registry needed. - Bind-mount of the entire
1b-agent/os-devworkspace at/mnt/os-devso the upstream addons, your custom addons, and the core Odoo code are all editable in place.
2. Components
| File | Purpose |
|---|---|
Dockerfile |
Verbatim copy of github.com/odoo/docker/19.0/Dockerfile + pip install -r requirements.txt --break-system-packages. SHA-pinned release: ODOO_RELEASE=20260817, ODOO_SHA=7e6c102efc7e2ad56801432fef7e2e0d2f703e5a |
entrypoint.sh |
Postgres env-var → --db_host/--db_user/... propagation (copied from upstream) |
wait-for-psql.py |
Waits for Postgres before starting Odoo (copied from upstream) |
odoo.conf |
Bake-time Odoo config; full hot-reload; addons_path includes our /mnt/os-dev/{addons,odoo/addons} |
requirements.txt |
Python deps from the upstream odoo/19.0 requirements.txt |
easypanel.schema.json |
Easypanel service payload using source.type=dockerfile + inline Dockerfile |
deploy.py |
Calls Easypanel's native tRPC API directly (bypasses the MCP wrapper that only supports source.type=image) to push the inline Dockerfile, set env, and deploy |
.gitea/workflows/build.yml |
Optional: auto-build & push os-dev:19.0 to a Gitea Container Registry on every push to 1b-agent/os-dev's 19.0 branch |
3. How it was built (research summary)
3.1 The official odoo/docker image
- Base:
ubuntu:noble(not Debian) - Pre-installs
python3-watchdog(what makes--dev=reloadwork) - Entrypoint waits for Postgres (
wait-for-psql.py --timeout=30) - Default
odoo.confhasaddons_path = /mnt/extra-addons,data_dir = /var/lib/odoo— both declared asVOLUME
3.2 dev_mode flags (Odoo 19 CLI reference)
| Flag | Effect |
|---|---|
reload |
restart odoo-bin on .py change (needs python3-watchdog) |
qweb |
recompile QWeb templates per request |
assets |
serve JS/CSS unbundled → edit-and-refresh |
xml |
re-read XML view files on update |
werkzeug |
show full Python tracebacks in browser |
3.3 Why source.type=dockerfile (not image pull)
Easypanel's services.app.createFromSchema (the MCP wrapper) only
supports source.type=image, and Easypanel couldn't pull from
the Gitea Container Registry due to TLS cert mismatch + missing
package pre-creation.
Easypanel itself supports source.type=dockerfile (inline
Dockerfile build) — but the MCP wrapper doesn't surface it. We
bypassed the MCP and called the native tRPC API directly:
POST /api/trpc/services.app.createService
POST /api/trpc/services.app.updateSourceDockerfile
POST /api/trpc/services.app.updateEnv
POST /api/trpc/services.app.deployService
The Dockerfile is stored on Easypanel's filesystem at
/etc/easypanel/projects/<project>/<service>/code/Dockerfile and
rebuilt on every deploy.
3.4 Self-contained inline Dockerfile
Easypanel's source.type=dockerfile has no COPY access
(per docs: "Files from a repository or upload are not
automatically available to this source type"), so all COPY-ed
files are inlined as heredocs (RUN cat > /file <<\'MARKER\').
This produces a single ~17 KB Dockerfile with zero COPY
instructions.
4. Reproducing this setup on a fresh Easypanel
4.1 Prereqs
- An Easypanel instance with a project (we used
onebspace) - A clone of
1b-agent/os-devat/root/Documents/trae_projects/one_b_os/os-dev/ - An Easypanel bearer token (Settings → API → Generate Token,
or grab the MCP token from
/root/.trae-server/data/Machine/mcp.json)
4.2 Run the deploy script
cd stateless-container-for-onebspace-development
python3 deploy.py deploy
This will:
- Generate the inline Dockerfile from
../os-dev/*files - Generate a fresh DB password and save to
.db_password - Patch
CHANGE_ME_DB_PASSWORDin the env block - Call the four Easypanel tRPC procedures in order
- Build the image in-place and start the container
Verify:
curl -sS -o /dev/null -w "HTTP %{http_code} bytes=%{size_download}\\n" \
http://127.0.0.1:18069/web/database/manager
# expected: HTTP 200 bytes=70194
5. Daily workflow
# edit code in place — auto-reload picks it up
vim /root/Documents/trae_projects/one_b_os/os-dev/odoo/addons/web/controllers/main.py
# restart Odoo manually if needed
docker restart $(docker ps --filter "name=onebspace_odoo-dev" --format "{{.ID}}" | head -1)
# rebuild the image (only when requirements.txt or Dockerfile changes)
python3 deploy.py redeploy
6. Verified working state
Odoo version 19.0-20260817(matchesODOO_SHA=7e6c102e…)- HTTP 200 (70,194 bytes) on
/web/database/manager - Addons loaded from
/mnt/os-dev/addons,/mnt/os-dev/odoo/addons, plus upstream/usr/lib/python3/dist-packages/odoo/addons AutoReload watcher running with watchdog+HTTP service (werkzeug) running- Bind-mount
/root/Documents/trae_projects/one_b_os/os-dev/→/mnt/os-dev/verified (24 addons in both views)