Skip to main content

Container Components

Container components are stack-scoped, persistent containers. One component is one service. Atmos owns the image artifact (build/push/pull) and an optional long-running named container lifecycle, discovered by labels derived from the canonical component instance address — not from local state files. You declare them with the same stack-based configuration used for Terraform, Helmfile, Packer, and Ansible.

For the conceptual overview and command walkthrough, see Using Containers. This page is the stack-manifest configuration reference.

Available Configuration Sections​

Container components are declared under components.container and use first-class sections (siblings of metadata — not nested under vars):

image
The image reference to run (and the default push/pull target). May be set directly or produced by build.
build
How to build the image — context, dockerfile, tags, build_args, target, no_cache, pull, and more. Reuses the same shape as the type: container workflow step.
run
How to run the long-lived container — command, ports, mounts, user, run_args, pull, restart, and healthcheck.
env
Container application environment, resolved with secrets. Injected into the running container — not read from run.
composition
Optional composition membership — groups this service into a larger system.
metadata
Component behavior and inheritance (e.g. metadata.type: abstract for catalog base components).
hooks
Lifecycle event handlers.

Component Structure​

A typical container component configuration:

components:
container:
api:
composition: storefront # composition membership (optional)
image: localhost:5001/api:latest
build: # build the image
context: app
dockerfile: Dockerfile
tags:
- localhost:5001/api:latest
run: # run configuration
command: ./api
ports:
- host: 8080
container: 80
mounts:
- source: .
target: /workspace
restart:
policy: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/health"]
interval: 30s
retries: 3
env: # component env (resolved with secrets)
PORT: "8080"

Inheritance (metadata.inherits), catalogs, mixins, and deep-merge work exactly like other component kinds — define an abstract base component with shared run/build defaults and inherit it.

Component-Type Defaults​

Define defaults for all container components:

# Apply to all container components
container:
run:
restart:
policy: unless-stopped

# Individual components
components:
container:
api:
image: localhost:5001/api:latest

Complete Example​

stacks/deploy/dev.yaml
import:
- catalog/container/_defaults

vars:
stage: dev

components:
container:
api:
composition: storefront
build:
context: app
dockerfile: Dockerfile
tags:
- localhost:5001/api:latest
run:
ports:
- host: 8080
container: 80
env:
PORT: "8080"

worker:
image: localhost:5001/worker:latest
run:
command: ./worker
env:
QUEUE_URL: "redis://localhost:6379"

Running Container Commands​

Atmos provides commands that wrap the container lifecycle:

atmos container build api -s dev # build the image from `build`
atmos container up api -s dev # create/start the long-running container (build-on-missing)
atmos container list # all container components + running state
atmos container ps api -s dev # show running state
atmos container logs api -s dev # stream logs
atmos container exec api -s dev -- sh # run a command inside the container
atmos container down api -s dev # stop + rm

Each instance is named and labeled from its canonical address <stack>/container/<component> (e.g., atmos-dev-container-api), so lifecycle commands discover it by label — there are no local state files.

Containers in the same stack automatically join a shared network and can resolve each other by a <stack>-<component> DNS alias, with no configuration required — see Networking.

CI Reporting​

When CI is enabled, atmos container build and atmos container push write a summary for each image to the job summary: the tag, digest, image ID, runtime settings, environment variables, labels, and layers. The job summary always keeps the full report, and every pushed reference gets its own section.

Pull request comments​

The same summary can also be posted as a pull request comment. Atmos posts a comment only when all of these are true:

  • ci.enabled and ci.summary.enabled are on, and Atmos runs under a detected CI provider, so the summary reached the provider.
  • ci.comments.enabled is true. Comments are off by default because they write to the pull request.
  • A pull request number is known. When none is known, for example on a push to a branch, the comment is posted as a commit comment on the run's commit instead.
  • The job token can write comments. On GitHub Actions, grant pull-requests: write, and contents: write for commit comments.

Atmos keeps one comment per image, not one per tag. The comment key is container:image:<registry/repository> with the tag and digest removed, so ghcr.io/acme/api:sha-1a2b3c and ghcr.io/acme/api:latest update the same comment. A build or push replaces the body in place, and a push of several tags lists every reference pushed in that run. Images with different repositories, for example one pushed to two registries, get separate comments.

GitHub rejects comment bodies over 65,536 characters. Atmos truncates the comment body at 65,000 characters and ends it with a note that points to the job summary, which keeps the full report. A comment that cannot be posted logs a warning that names the image and never fails the build or push.

When comments are off, Atmos does not post a comment and does not print a preview of one to the log. Outside CI, the summary is rendered locally as before.

To change the Markdown, point ci.templates.container.image at a template file under ci.templates.base_path:

atmos.yaml
ci:
templates:
base_path: ".atmos/ci/templates"
container:
image: "image.md"

See CI templates for the template syntax and the data available to the image template.