Projects
August 14, 2026 · View on GitHub

A Project is a managed Compose folder: a compose file plus its sidecar
files (configs copied into containers, .sh scripts, init files, …) that Docker
Commander stores and edits for you, then deploys by running the real
docker compose CLI on the host. Because it uses the CLI, you get the full
Compose feature set — depends_on, profiles, build:, configs, init
containers — for free. A deployed project also appears on the
Stacks page, where its lifecycle and "view compose" live.
The Compose CLI runs where Docker Commander runs; the daemon it talks to can be anywhere. A project targets the local daemon by default, or any host you've added under Hosts — the CLI is pointed at it with
DOCKER_HOST(plus the host's TLS certs, orssh://). A project's target host is its own setting and does not follow the sidebar host switcher. See Deploying to a remote host.Deploy/Down are disabled (with a note) if the
docker composeCLI isn't installed on the Docker Commander machine — that is the one place it has to exist, whichever daemon you deploy to.Running under systemd and Deploy/Down are disabled even though
docker composeworks in your shell? It's theProtectHome=truehardening — see the fix in Deployment → Running as a service.
Creating a project

Give the project a name, pick the host to deploy to (the local daemon or any host you've added), then choose how to scaffold it. An identifier — the slug — is derived from the name, lowercased with diacritics transliterated. The files are always rendered and written server-side:
- Template — start from a ready-made preset (e.g. Nginx — static site,
Nginx + Postgres + Adminer, LEMP (Nginx + PHP + MySQL), Node +
Postgres + Redis), or Empty for a bare starter
compose.yml. Presets can declare variables (ports, database names, passwords) you fill in on a small form; blank fields fall back to a default, andsecretones can be auto-generated. - Builder (the skládačka) — tick the service blocks you want — Nginx,
PHP-FPM, Node, Postgres, MySQL, Redis, Adminer — and
they're merged into one
compose.ymlyou can edit afterwards. Add your own with Custom service… (name, service key, the service YAML, optional named volumes); it's saved and reappears in the builder. Under Shared definitions you can also include reusable top-level YAML anchors (e.g.x-pg-common: &pg-common …) — emitted aboveservices:so a cluster of services can share one definition (security, cert mounts, …) and merge it with<<: *pg-common. Built-ins (Service defaults, Secured Postgres) ship in, and you can save your own with Custom definition…. - Import — choose a
.zipto import an existing project folder (files are written through the same path sandbox).
As you pick a template or builder blocks, a live read-only preview of the
resulting compose.yml renders alongside the form, so you see what you'll get
before creating the project.
Save as preset — the editor's 🗎 button snapshots the open project's files into a reusable preset that then shows up under Template (and on the Templates page). Built-in presets and blocks are read-only; the ones you save are yours to edit or remove.
Built-in presets/blocks ship with the binary; saved ones live in the data dir. A future catalog source could pull presets from a remote API.
Managing templates

The Templates page (sidebar, under the Projects permission) is where your presets and builder blocks live:
- Presets — edit a saved preset's files in the same multi-file editor,
rename it / change its description, download it as a
.zip, or delete it. Built-in presets open read-only so you can inspect what they scaffold. - Service blocks — create a block, edit an existing one (name, service key, the service YAML, named volumes), or delete it; built-in blocks are read-only. Blocks you add here (and via the builder's Custom service…) appear in the builder.
- Shared definitions — create/edit/delete top-level YAML anchors (see the Builder above); built-in ones are read-only. They appear in the builder's Shared definitions list.
Built-in presets/blocks can't be modified; only the ones you save are editable.
The editor

A modal with a file tree on the left and a CodeMirror editor on the
right, with syntax highlighting for YAML, JSON, shell, Dockerfiles and
.conf / .env files.
- New file / New folder / Upload create inside the current folder — click a folder (or open a file) to set it as the target; the toolbar shows where new items land, with an × to go back to the project root. Upload accepts binary/data files too (shown download-only in the tree).
- Save writes the open file; an unsaved-changes dot marks edits.
- Image autocomplete — on a compose
image:line, suggestions appear for repository names (your locally-pulled images first, then a Docker Hub search) and, once you type a:, for that repo's tags (local tags + Docker Hub). The Create-container form's image field offers the same. It's best-effort — offline you still get your local images. For a host you've added under Registries, tag suggestions also come from that private registry's API (using its stored credentials); hosts you haven't configured are never contacted. - Compose autocomplete — in a Compose file you also get schema-aware
suggestions: top-level keys, service keys (at the right indent), nested
build/healthcheck/deploy/loggingkeys, and known enum values (e.g.restart:→always/unless-stopped). Press Ctrl+Space to pop the list on a blank line. It's a typing aid, not a validator — the authoritative check is stilldocker compose config(the inline diagnostics). - Download a single file (next to Save) or the whole project as a
.zip(editor header). - Profiles — if the compose file defines
profiles, a toggle bar lets you pick which ones to enable; the selection is remembered and applied on deploy. The bar also shows Deployed with: …, the profiles actually used on the last successful deploy — separate from the toggle chips above it, which are only what's selected for the next deploy. The Summary panel (below) badges each service against that deployed set, so a service excluded by it reads "Not in active profile" rather than "Stopped".
Validation (live, while you edit)
Validation runs on the unsaved buffer (no save needed) and shows results as inline diagnostics underlined on the relevant line, plus an at-a-glance status chip:
- Compose files —
docker compose config(the real deploy parser, so YAML anchors, merge keys<<,${VAR}interpolation andextends/includeresolve as atuptime). Unset-variable warnings are surfaced too. - Dockerfiles —
docker build --check(BuildKit's linter; no build runs). - YAML / JSON /
.env— instant client-side syntax lint.
On the compose file, two extra actions sit in the editor toolbar:
- Resolved — the fully-flattened compose (anchors / interpolation / extends
resolved) — exactly what
docker compose updeploys. - Summary — an overview of services, published ports and volumes, with a duplicate-host-port check and a per-service state badge (Running / Partial / Stopped / Not in active profile / Not deployed) computed against the project's live containers and the profiles actually deployed.
Lifecycle
-
Deploy / Redeploy — runs
docker compose up -d --build(with the selected profiles) on the project's target host. Redeploy re-applies after edits. The combined output is shown.--buildis what makes "what's in the editor is what runs" true for a project with abuild:section. Plainup -dbuilds a service only when its image is missing, so without it the second deploy after editing a Dockerfile — or any file in its build context — would silently keep running the old image and report nothing worse thanContainer Running. It is a no-op for services that only pull an image, so an image-only project pays nothing for it. Callers that would rather not re-send a large context canPOSTthe deploy with{"build": false}. -
Down —
docker compose downon the target host (available once deployed). -
Settings — changes the display name and the target host; the slug / compose project name stays fixed, so deployments remain stable.
-
Delete — refuses while the project is deployed (offers to bring it down first); deleting the last file offers to delete the now-empty project.
Deploying to a remote host
A project can target the local daemon (default) or any remote host you've
added under Hosts — pick it when creating the project or via its
Settings. Deploy/down/restart then run docker compose against that host's
daemon (over TCP, with the host's TLS certs, or SSH).
Bind mounts on a remote host
A remote daemon can't see files inside Docker Commander's own data dir, so a
bind mount can't be handed to it as-is. On a remote deploy each bind mount whose
source lives inside the project folder (e.g. ./html:/usr/share/nginx/html,
./nginx.conf:/etc/nginx/nginx.conf) is therefore copied to a named volume on
that host and the mount is repointed at it. Sidecar configs and scripts work on
a remote host the same way they do locally, with three things worth knowing:
- It's a snapshot, not a live mount. The files are copied at deploy time. Editing them in the project afterwards needs a redeploy, and writes made inside the container stay in the volume on the remote host — they don't flow back into the project files. (A local deploy still mounts the folder directly, so there it is live.)
- Only paths inside the project folder are shipped. A bind mount pointing
outside it (
/etc/localtime,/var/run/docker.sock, anything reached via a symlink out of the folder) names a path on the remote host, so it's refused with a message listing the offending mounts rather than mounted blind. If that is genuinely what you want, tick Allow host paths in the project's Settings: those mounts are then taken from the remote host's own filesystem, with whatever they hold there, and nothing is copied. The deploy output names them every time. Enabling it needs write access to the Hosts section — it is authority over the host, not over the project — and is recorded in the audit log. - The seeded volumes are named
dcseed-<project>-<hash>and are labelled with the project, so they're easy to spot on the Volumes page. Like any named volume they survive adown. Deleting the project offers to remove them — it lists them and asks, since they hold data; declining keeps them for a later redeploy.
Changing a deployed project's target host offers to bring it down on the host it is leaving — ticked by default, because "change the host" usually means move, not run a second copy. What goes: the stack's containers are stopped and removed there, and the volumes seeded for its bind mounts are deleted. Named volumes holding your data are left alone, and nothing is started on the new host — deploy it when you are ready.
Untick it and the old copy keeps running while this page shows only the new host, so you have two live deployments. That is a legitimate thing to want; it is just no longer what happens by accident.
If the teardown fails — the old host is unreachable, say — the project is not moved. Leaving a running stack on a host the app no longer points at is the same problem, only invisible.
build: contexts on a remote host
A project that builds its own image works against a remote daemon without any extra setup, and it's worth knowing why, because it is not the same mechanism as bind mounts:
- A build context is uploaded by Docker itself. The build API takes the
context as a tar stream from the machine running the CLI, so the remote daemon
receives your local
./appfolder even though it can't see your filesystem. The image is built on the remote host and exists only there. - A bind mount is not uploaded by anything, which is why Docker Commander seeds it into a volume on the target host and repoints it with a generated override (above).
The two combine in one deploy — a project can build an image and mount sidecar configs — and a redeploy refreshes both: the image is rebuilt from the edited context, and the seeded files are re-copied.
One consequence worth remembering: the build runs on the target host's architecture and daemon. A project that builds fine on your amd64 laptop can fail on an arm64 host, and the error comes back in the deploy output.
Tips
- Sidecar files are referenced from the compose file relative to the project
folder (e.g.
./html:/usr/share/nginx/html), so configs/scripts land inside the containers exactly as the CLI would mount them. - Restarting a deployed project's containers (without re-applying files) is available on the Stacks page.