Skip to content

docker deploy provider: work when core runs in a container - #858

Open
irisfeng wants to merge 1 commit into
yc-software:mainfrom
irisfeng:fix/docker-deploy-provider-containerized-core
Open

docker deploy provider: work when core runs in a container#858
irisfeng wants to merge 1 commit into
yc-software:mainfrom
irisfeng:fix/docker-deploy-provider-containerized-core

Conversation

@irisfeng

@irisfeng irisfeng commented Aug 30, 2026

Copy link
Copy Markdown

Problem

qm up on the docker backend runs every service — including core — as a container on one shared stack network, with core's /data as a named volume and (when the local sandbox backend is active) the docker CLI plus DOCKER_HOST/QM_CORE_CONTAINER wired into core. That is the shape self-hosted deployments get out of the box, and the docker deploy provider (the default DEPLOY_PROVIDER) assumes the opposite of it in two places:

  1. Snapshot bind mount. apply ran the app container with -v ${snapshotDir}:/app:ro. The snapshot lives inside core's container/volume; the host daemon resolves that path against the host filesystem and auto-creates an empty directory there — every published app starts with no code and dies on MODULE_NOT_FOUND. Verified on a live deployment: the host-side directory is empty, the core-container-side one has the app.
  2. Loopback endpoint. apply published the app port on 127.0.0.1 and returned {host: "127.0.0.1"}. A containerized core dialing loopback reaches itself, never the app — so even a running app was unreachable.

Fix

  • Snapshot materialization becomes docker createdocker cp <snapshotDir>/. <container>:/appdocker start. docker cp streams through the daemon API from wherever the docker CLI runs, so no filesystem boundary is crossed — correct for container-run and host-run core. The copy gets a generous explicit timeout (snapshots routinely carry node_modules; the 60s default would kill large copies mid-stream).
  • Shared-network mode. A new DEPLOY_DOCKER_NETWORK env names the network core already sits on. qm up sets it for core alongside DOCKER_HOST/QM_CORE_CONTAINER — those three now come from one shared helper so the serviceEnv and dockerServiceEnv code paths cannot drift apart again. Each app container joins that network with --network-alias <container-name> and no host port publishing; the endpoint becomes {host: <container-name>, port: APP_PORT}, resolved through docker's embedded DNS. destroy never removes a network it did not create. Deployments stored with a pre-fix loopback endpoint resolve as absent so the deploy service re-materializes them onto the shared network.
  • Without DEPLOY_DOCKER_NETWORK, the provider keeps the previous per-deployment network + published loopback port shape, which is correct for core running directly on the host.

Endpoint consumers already treat host as an opaque hostname (the proxy dials host:port from core), so no consumer changes were needed.

Tests

Provider: shared-mode sequence (create → cp → start, no -v/-p, alias endpoint, network untouched on destroy), copy-failure and start-failure cleanup, stale-loopback re-materialization, plus new pins on the fallback path (loopback publish + snapshot copy). CLI: the existing "host daemon coordinates only into core" test now also pins DEPLOY_DOCKER_NETWORK. Full provider suite 9/9, auth-broker 11/11, CLI suite identical to the pre-change baseline (same env-gated failures).


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

The docker deploy provider assumed core runs on the host: it
bind-mounted the snapshot directory (a path inside core's container,
resolved by the host daemon against the host filesystem — an
auto-created empty dir, so every published app died on MODULE_NOT_FOUND)
and returned 127.0.0.1 endpoints (host loopback, which a containerized
core cannot reach).

Materialize snapshots with docker create + docker cp + docker start
instead: docker cp streams through the daemon API from wherever the
docker CLI runs, so no filesystem boundary is crossed. The copy gets a
generous timeout — snapshots routinely carry node_modules and the
default exec timeout would kill large copies mid-stream.

Add a shared-network mode: when DEPLOY_DOCKER_NETWORK names the network
core already sits on, each app container joins that network with a
network-alias equal to its container name and the endpoint becomes
{host: <container-name>, port: APP_PORT} — resolvable through docker's
embedded DNS, no host port publishing. qm up sets the variable for core
next to DOCKER_HOST/QM_CORE_CONTAINER, extracted into one shared
helper so the serviceEnv and dockerServiceEnv code paths cannot drift
apart again. Deployments stored with a pre-fix loopback endpoint
resolve as absent so the service re-materializes them onto the shared
network. Without the variable the provider keeps the previous
per-deployment network + published loopback port shape for host-run
core.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant