Skip to content
Bart Reardon edited this page Aug 9, 2026 · 2 revisions

Stacks

A stack is several containers stood up together as one unit — a web app and its database, say. container itself has no compose concept; a stack is a ContainerManager convenience that creates the pieces, wires them together, and labels them so they can be managed as a group.

What creating a stack does

Create a stack called mysite and the app:

  1. Creates a private NAT network, mysite-net, labelled with the stack.
  2. Creates the named volumes its services need, labelled with the stack, so they're recognisable later even if the stack is gone.
  3. Validates the whole plan — mount specs, port specs, missing host paths — before creating anything, so a mistake in the fourth service doesn't leave the first three running.
  4. Creates and starts each service in dependency order, named mysite-<role>, attached to the network.
  5. Substitutes real addresses into each service's environment as it goes (below).
  6. Saves a definition of what it built, and a log of how it went.

Every container carries a com.containermanager.stack label, which is how the lists group them. They also appear individually under Containers.

How services find each other

By default, containers on a container network reach each other by IP address — nothing resolves container names until a DNS domain is configured, and the app can't assume one, so it substitutes addresses instead of relying on names.

(If you have set up Local DNS, names do resolve between containers. Stacks still wire themselves with addresses, because a stack has to work whether or not you've done that.)

A definition refers to another service with a token:

WORDPRESS_DB_HOST=${IP:db}:3306

At create time the db service is started first, its address is read, and ${IP:db} is replaced with it. No DNS, no copying addresses by hand.

Addresses change when containers restart, which would ordinarily break the wiring. Starting a stack reconciles it: services come up in dependency order, and any whose recorded addresses have gone stale are re-created with the current ones. Only the address entries are rewritten — anything you've edited by hand survives.

Two consequences worth knowing:

  • A service started on its own, outside the stack, isn't reconciled.
  • Anything a service records about itself during setup — a "site URL" written into a database on first run — isn't ours to rewrite. Point those at localhost:<published port>, which survives a restart.

With Local DNS configured you can sidestep all of this in your own service definitions by using names — db.test rather than ${IP:db} — which survive restarts and need no reconciling. The trade is that the stack then only works on a Mac where DNS has been set up, which is why the bundled templates don't do it.

Templates

Stacks ▸ + lists what ships with the app:

Template What it is
WordPress + MariaDB Blog/CMS with its database, both on volumes
PostgreSQL A database on a volume, published for an app on your Mac
PostgreSQL + Adminer The above plus a web UI to browse and query it
Mailpit Local SMTP server with a web inbox, for testing outbound mail
Gitea Self-hosted Git server with a web UI and git-over-SSH
code-server VS Code in a browser, with a persistent /config volume
Nginx + host folder Serve a folder from your Mac as a static site

Below them are any templates you've imported, then Custom Stack…, then Import Template… and Show Templates Folder.

The bundled templates are ordinary .containerstack documents — the same format you can export, edit and import.

Custom stacks

Custom Stack… builds one from a web service plus an optional database service. If the database is enabled you choose the variable to inject its address into — Inject DB address into web as, default DB_HOST — and the database starts first so the address is known.

The variable has to be one your image actually reads. The templates know the right names; a custom image may want something else.

Managing a stack

Select a stack for a detail pane with:

  • Start / Stop / Delete in the toolbar. Both Start and Stop stay put and enable or disable rather than swapping, because a stack containing a one-shot init service is never all running.
  • Appearance — a display name and icon of your choosing, so wordpress-2 can read as "Client site".
  • Services — each with its status, image and address. Right-click for a terminal, or Replace… to change a service's image, command, environment, ports or mounts. The replacement inherits what the old one had, so you can edit one field.
  • Add Service… — add a container to a running stack. It joins the network, gets the labels, and can use ${IP:role} tokens against the services already up.
  • Network and Volumes — what the stack owns.
  • Log — how the stack was built, and anything an import couldn't carry over.

When a service is missing

If a service in the saved definition isn't running, the detail pane says so and offers Re-create, which rebuilds it from the settings it was created with. This is the usual outcome of a service that failed to start the first time.

Services added later with Add Service… aren't in the saved definition, so they aren't tracked this way.

Deleting

Delete removes every container in the stack and its network. Volumes are kept — a database shouldn't disappear because you deleted the stack that used it. Remove them from Volumes when you actually want the data gone.

Adding an existing container to a stack

Containers get their stack label when they're created; the runtime has no way to change labels afterwards. So an existing container can't be moved into a stack — it has to be re-created.

Two ways to create one in a stack:

  • Containers ▸ +, and pick the stack in the Stack row. It joins the stack's network automatically.
  • Stacks ▸ the stack ▸ Add Service…, which additionally lets you use ${IP:role} tokens.

Both mean the container is started and stopped with the stack, and deleted with it.

Volumes work the same way — Volumes ▸ + has a Stack picker — except that deleting a stack keeps its volumes. A volume that already exists can be grouped after the fact: right-click it and use Set Label…, which the app records itself.

After a reboot

Stacks come back stopped. Start them from the Stacks list — the reconcile step handles whatever addresses have changed.

Clone this wiki locally