- internal/deployqueue: serialized FIFO per service — concurrent deploys
of one service no longer race (the container-name-conflict failure
mode); different services still run in parallel.
- Deploys and rollbacks enqueue through the queue; a busy service
returns status 'queued' and promotes when the active job finishes.
- POST /deployments/:id/cancel — queued jobs drop immediately
(200 cancelled); active jobs get ctx cancelled so build/reconcile
abort (202 cancelling, settles 'cancelled'); finished deployments
return 409 NOT_CANCELLABLE.
- Redeploy and variables save now route through the same queue so they
can't overlap a build; PUT /services/:id/variables accepts
redeploy:true to apply env changes to running containers.
- no_cache on deployment create plumbs BuildRequest -> BuildOptions for
nixpacks, dockerfile and railpack builders.
- CLI: deployments cancel, deploy --no-cache, variables set/unset
--redeploy. MCP: containr_deployments_cancel; deploy/variables_set
document no_cache/redeploy. UI: Cancel button + queued/cancelled
badges on the deployments table. OpenAPI + types regenerated.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: e2e QA hardening — honest runtime URLs, health, and read-only anon UI
Backend:
- managed DB provisioning: attach DB containers to the platform network and
probe the container IP, fixing the 127.0.0.1 reachability failure inside
the backend container; log provisioning failures
- stop fabricating connection URLs when provisioning produced none
- template deploys now carry port + health_check into service runtime fields
- service health probed over the project network (backend joins it) instead
of an in-container wget that fails on shell-less images
- runtime/public/connection URLs rewrite loopback to the request Host so
links are usable for LAN/remote clients
Frontend:
- service Networking panel shows real runtime URLs/ports/health instead of
hardcoded containr.dev + 8080->443
- anonymous views hide all mutation controls (new project, add service,
group, context menu)
- project cards: remove decorative svcN labels; badge says DOWN only when
zero services run, IDLE when empty, DEGRADED for partial
- mobile projects header no longer clips (fluid search width)
- unknown routes render a not-found page instead of silently redirecting
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: telemetry scope label, chunk split, remove stale vite artifacts
- /system/host now reports scope=host|docker-desktop-vm and the daemon OS;
Projects dashboard labels cards VM.* when the daemon is a Desktop VM so
memory/core numbers aren't mistaken for physical host values
- manualChunks: split @xyflow/react and recharts out of the main bundle;
removes the >1MB chunk warning
- drop tracked vite.config.js/.d.ts build artifacts that shadowed the real
vite.config.ts
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: persist service host port; demo-data scope for telemetry
Public URLs were ephemeral: every restart/redeploy rebound a random host
port, so endpoint links rotted. Persist published_port on the service and
prefer it for replica 0 on create. If the port is taken, fall back to
ephemeral and repersist the new assignment. Post-start the binding is
verified — Docker Desktop accepts a conflicting publish silently, which
previously left services running with no endpoint at all.
demo-data.ts gains the required scope field for HostMonitoring (CI fix).
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Backend:
- tolerate concurrent project-network creation (re-resolve on conflict)
- always publish an ephemeral host port when a port is configured, even
with a domain set — Traefik routes on top when the edge network exists
- public_url now prefers the configured domain, falls back to the first
published port
- service logs resolve replicas by containr.service label and merge all
replica streams — previously queried a nonexistent containr-<id> name
Canvas/detail:
- degraded status renders amber end-to-end (node card, detail badge,
status dot) instead of falling through to "Stopped"
- edges pick handle sides by relative node position — no more backward
loops when the source sits right of the target
- context menu gains Start for stopped services, Visit uses the
published URL when no domain is set, menu clamps to canvas bounds
- node cards show the image tag instead of a redundant hint
- scaling tab falls back to live runtime replica counts when no
autoscaler state exists; manual scale no longer requires it
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Services now reconcile into actual Docker containers instead of
bookkeeping rows:
- Per-project bridge network with service-name DNS aliases and
${{service.KEY}} env expansion (HOST/PORT builtins)
- Replicas, published ports, Traefik domain routing, health checks,
restart policies persisted on the service spec; spec-hash drift
recreates containers on redeploy
- Lifecycle API: start/stop/restart/redeploy, live runtime state,
real image rollback (replaces the fake sleep)
- Manual scale and PUT replicas reconcile live containers; service
and project delete remove their containers and the project network
- Canvas: real URLs, replica badges, env filter, right-click
deploy/restart/stop/delete menu, empty state
- Service detail: real lifecycle buttons, live replica/URL strip,
Networking editor, ${{ }} variable helper
- Fix prebuilt-image pulls racing the tag (drain the pull stream)
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>