1bspace-os rebrand 6e7be43dc8 feat(codespace): replace broken odoo-image blueprint with python:3.12-slim pattern
The previous blueprint (upstream odoo/docker:19.0 + COPY + deploy.command)
systematically broke inside Easypanel's inline-dockerfile build context:
  - COPY files not available
  - wkhtmltopdf/libldap-2.5-0 package names absent from Debian bookworm
  - Easypanel wrapped deploy.command with 'python -c …' → exit 100
  - postgres password / app env set separately → mismatch auth failure
  - cat > /etc/odoo/odoo.conf without parent dir → ENOENT

The new blueprint is what actually ran in production for 34h+ stable:
  - FROM python:3.12-slim with an inline ENTRYPOINT (no CMD wrapping)
  - init-odoo.sh: apt install build deps then pip install /mnt/os-dev
    (idempotent — skipped if import odoo works, restarts < 1 s)
  - --dev=reload,qweb,assets,xml,werkzeug baked into exec odoo
  - mkdir -p /etc/odoo /var/lib/odoo /var/log/odoo before writing odoo.conf
  - Single createFromSchema call → shared password for postgres + app
  - Bind-mount os-dev → /mnt/os-dev → hot-reload on any host edit

deploy.py prepare writes easypanel.schema.json + init-odoo.sh +
inline.Dockerfile + .db_password, all ready for TRAE Easypanel MCP
createFromSchema + deployAppService. Verified end-to-end (HTTP 200 on
/web/database/manager, hot-reload on release.py product_name edit,
CREATE DATABASE via psycopg2 + odoo UI Create Database).

Docs:
  - README.md rewritten with new layout table, troubleshooting
    playbook, daily workflow, verified-state reference table,
    old-vs-new blueprint comparison table.
2026-09-14 22:59:46 +00:00

stateless-container-for-onebspace-development

Blueprint (and reusable scripts) for spinning up a stateless hot-reload os-dev codespace for the 1b-agent/os-dev Odoo 19 fork, deployed on the Easypanel project onebspace.

This blueprint was battle-tested Sep 2026 on a real deployment: the container stayed Up 34 hours+ stably running Odoo 19 from a bind-mount of the live source tree with all five --dev=* flags active, and we proved end-to-end hot-reload by editing release.py product_name and watching the change propagate without a restart.

1. What this gives you

  • Zero image rebuilds on code edits. Edit any .py / .xml / .js in os-dev/ and the change is live within milliseconds because Odoo is launched with --dev=reload,qweb,assets,xml,werkzeug.
  • Bind-mounted source. The whole os-dev repository is mounted from the host into the container at /mnt/os-dev. First boot takes ~5 min while pip install /mnt/os-dev builds native extensions; every subsequent boot is < 1 s (idempotent install).
  • Tiny base image. python:3.12-slim (Debian bookworm, ~45 MB) instead of the bloated upstream ubuntu:noble Odoo image.
  • Trivial inline Dockerfile (80 lines, 2 RUN instructions total, zero COPY instructions — every file is a heredoc). This is crucial because Easypanel's source.type=dockerfile build context contains only the Dockerfile; no repo files are available to COPY.
  • Single schema call. Passwords for postgres AND the app env are generated from the same createFromSchema payload, so the "FATAL: password authentication failed for user odoo" mismatch bug cannot happen again.
  • 2 UI extras on postgres: pgWeb + dbGate web panels are enabled out of the box on the companion db service.

2. What changed from the old blueprint

The previous blueprint tried (and failed) to replicate the upstream odoo/docker:19.0 Dockerfile with COPY'd files. Every single step of that approach broke inside Easypanel's inline-dockerfile build context:

Old approach (broken) New approach (works)
Upstream odoo:19.0 base (1 GB, ubuntu) python:3.12-slim (45 MB, bookworm)
COPY entrypoint.sh / COPY odoo.conf in Dockerfile Inlined as RUN cat > /file <<'MARKER' heredocs
wkhtmltopdf and libldap-2.5-0 apt packages (names don't exist in debian bookworm) Omitted (we don't use PDFs in dev) / renamed to libldap2-dev
deploy.command="bash entrypoint.sh" (Easypanel wraps it as python -c "bash…" → exit 100) Explicit ENTRYPOINT ["/usr/local/bin/init-odoo.sh"] in inline Dockerfile
Updated app env password AFTER postgres service was already running → password mismatch Shared password in single createFromSchema payload
cat > /etc/odoo/odoo.conf without creating the parent dir → ENOENT → odoo.conf doesn't exist or is not readable mkdir -p /etc/odoo /var/lib/odoo /var/log/odoo first
Manual tRPC calls (services.app.createService etc.) bypassing TRAE MCP Easypanel MCP createFromSchema (handles ordering + postgres)

3. Repo layout

File Purpose
deploy.py One-stop script. python3 deploy.py prepare writes all artifacts + a fresh DB password. dockerfile subcommand prints the generated inline Dockerfile for inspection.
init-odoo.sh Runtime entrypoint. Written by deploy.py prepare. Installs apt deps, then pip-installs the bind-mounted /mnt/os-dev if odoo is not yet importable, writes odoo.conf from env vars, waits for postgres, then execs odoo with all 5 dev flags.
inline.Dockerfile The 80-line inline Dockerfile (written by prepare). Uses heredocs to drop init-odoo.sh and sets ENTRYPOINT explicitly.
easypanel.schema.json Ready-to-use createFromSchema payload (written by prepare). Contains both services, mounts, ports, env vars, and a fresh shared password.
.db_password Plaintext copy of the shared password (written by prepare). Git-ignored.
wait-for-psql.py Legacy artifact, no longer used (init-odoo.sh's socket-poll loop replaced it). Kept for reference.
entrypoint.sh Legacy artifact, no longer used (replaced by init-odoo.sh). Kept for reference.

4. Step-by-step: deploy a new codespace

4.1 Prerequisites on the host

  • Easypanel project onebspace exists.
  • A source checkout of os-dev at /root/Documents/trae_projects/one_b_os/os-dev/ (this is what gets bind-mounted — live edits inside it take effect immediately).
  • TRAE MCP Easypanel server is authenticated against http://45.137.70.123:3000 with a bearer token from /root/.trae-server/data/Machine/mcp.json.
  • Docker swarm mode (Easypanel runs swarm).

4.2 Run the prepare command

cd /root/Documents/trae_projects/one_b_os/stateless-container-for-onebspace-development
python3 deploy.py prepare
# → writes easypanel.schema.json, inline.Dockerfile, init-odoo.sh, .db_password
# → prints "Password: <fresh 24 chars>"

Optional sanity checks:

python3 deploy.py dockerfile | head -10     # confirm FROM python:3.12-slim
jq '.schema.services[0].data.password' easypanel.schema.json   # same password
jq '.schema.services[1].data.env' easypanel.schema.json        # PASSWORD=same value

4.3 Apply via TRAE Easypanel MCP

Inside a TRAE agent chat, do:

1. call  mcp_Easypanel : execute_mutation / createFromSchema
   payload = the json.read(easypanel.schema.json)
2. call  mcp_Easypanel : execute_destructive / deployAppService
   {projectName: "onebspace", serviceName: "os-dev-codespace"}
3. wait 5-7 min.  First boot runs pip install /mnt/os-dev → ~5 min
   native build (lxml, pillow, python-ldap, psycopg2-c).
4. curl -sS -o /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:18069/web/database/manager
   expect: HTTP 200 (70,194 bytes)
5. Visit http://127.0.0.1:18069/web/database/manager
   Master password = admin.  Click "Create Database".

4.4 Troubleshooting the container

If the service flips to Desired: Running, Current: exit 100 / exit 2, catch it in its 10-second window:

CID=$(docker ps --filter "name=onebspace_os-dev-codespace" --format '{{.ID}}' | head -1)
docker exec $CID cat /tmp/init.log 2>&1 | tail -50

The init script redirects everything to /tmp/init.log with set -x. Common fixes:

  • exit 100 on boot and no log written → the Dockerfile ENTRYPOINT was stripped / Easypanel wrapped it with python. Re-apply inline.Dockerfile via updateAppSourceDockerfile.
  • apt-get install had errors → check for package name drift (e.g. libldap-2.5-0 vs libldap2-dev). The script continues on apt errors; only a failed pip install matters.
  • config file '/etc/odoo/odoo.conf' doesn't exist or is not readable → you forgot mkdir -p /etc/odoo before the cat > redirect. It's in the current init script; if you see this, the script was not updated.
  • FATAL: password authentication failed for user odoo → you updated the app env but not the live postgres role. Don't do that. Always deploy both sides from the same createFromSchema payload, or if you must update the password live:
    docker exec -it $PG_CID psql -U odoo -d postgres \
      -c "ALTER USER odoo WITH PASSWORD '$NEW_PASS';"
    
  • Odoo installs but you can't create a database → verify your postgres user was CREATED with CREATEDB. The schema uses the postgres service type in Easypanel, which does this for you.

5. Daily workflow

# 1. Just edit code.
vim /root/Documents/trae_projects/one_b_os/os-dev/odoo/addons/...

# 2. Refresh the page.  --dev=reload re-imports modified modules on
#    next request; --dev=qweb re-renders templates; --dev=assets serves
#    unbundled JS.

# 3. Only if you edit requirements.txt / setup.py's install_requires AND
#    want to test the new deps WITHOUT waiting for a fresh image:
CID=$(docker ps --filter name=onebspace_os-dev-codespace --format '{{.ID}}' | head -1)
docker exec -it $CID bash -lc 'pip install --break-system-packages /mnt/os-dev'

# 4. Restart (NOT needed for .py edits, only for stale workers):
docker restart $CID

# 5. Destroy the whole stack when done:
#   (via TRAE MCP) destroyAppService + destroyPostgresService

6. Verified working state (reference)

Item Value (from the Sep 14 2026 live deployment)
Container image python:3.12-slim@sha256:6fb6b091a… (bookworm)
Container uptime Up 34 h (stable, no flapping)
Odoo PID 1 cmdline /usr/local/bin/python3 /usr/local/bin/odoo -c /etc/odoo/odoo.conf --dev=reload,qweb,assets,xml,werkzeug
Bind mount /root/Documents/trae_projects/one_b_os/os-dev → /mnt/os-dev (rw)
Filestore volume onebspace_os-dev-codespace-filestore → /var/lib/odoo
HTTP /web/database/manager 200 OK (70 194 bytes)
CREATE DATABASE test psycopg2 CREATE DATABASE test_codespace_db OWNER odoo — OK
Hot-reload test Edit release.py product_name='Odoo' → PRODUCT_NAME=1BOSDEV_HOTRELOAD_TEST inside running container within 10 s → revert → back to Odoo. No container restart.
Postgres postgres:17, user odoo, port 5432 (swarm DNS onebspace_os-dev-codespace-db)
Host port 18069 → container 8069
First-boot install time ~5 min (native lxml, pillow, python-ldap builds)

7. Customizing

Open deploy.py and edit the block at the top:

EASYPANEL_PROJECT  = "onebspace"
APP_SERVICE        = "os-dev-codespace"
HOST_PORT          = 18069
OS_DEV_HOST_PATH   = "/root/Documents/trae_projects/one_b_os/os-dev"
BASE_IMAGE         = "python:3.12-slim"
PG_IMAGE           = "postgres:17"

Then run prepare again to regenerate all artifacts.

S
Description
Stateless hot-reload Odoo 19 dev workspace deployed on Easypanel/onebspace. Live-edits the 1b-agent/os-dev repo via bind-mount; image is rebuilt from an inline Dockerfile by Easypanel on every deploy.
Readme
51 KiB
0 Stars 1 Watchers 0 Forks
Languages
Python 70.1%
Dockerfile 23.8%
Shell 6.1%