# 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](/components/container). 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](/workflows)
  .
- **`run`**
  How to run the long-lived container — 
  `command`
  , 
  `ports`
  , 
  `mounts`
  , 
  `user`
  , 
  `run_args`
  , 
  `pull`
  , 
  `restart`
  , and 
  `healthcheck`
  .
- **[`env`](/stacks/env)**
  Container application environment, resolved with 
  [secrets](/cli/configuration/secrets)
  . Injected into the running container — 
  **not**
   read from 
  `run`
  .
- **`composition`**
  Optional 
  [composition](/components/container#compositions)
   membership — groups this service into a larger system.
- **[`metadata`](/stacks/components/component-metadata)**
  Component behavior and inheritance (e.g. 
  `metadata.type: abstract`
   for catalog base components).
- **[`hooks`](/stacks/hooks)**
  Lifecycle event handlers.

## Component Structure

A typical container component configuration:

```yaml
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:

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

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

## Complete Example

**File:** `stacks/deploy/dev.yaml`

```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:

```bash
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](/components/container#networking).

## CI Reporting

When [CI](/cli/configuration/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`:

**File:** `atmos.yaml`

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

See [CI templates](/cli/configuration/ci/templates) for the template syntax and the data available to the image template.

## Related

- [Using Containers](/components/container) — Conceptual overview and command walkthrough
- [atmos container](/cli/commands/container/usage) — Command reference
- [Emulator Components](/stacks/components/emulator) — Built on the same container lifecycle
- [Component Types Overview](/stacks/components)
- [Configure Environment](/stacks/env)
