Skip to main content

Development Containers

Configure development container environments in your atmos.yaml to provide consistent, reproducible tooling across your team. Based on the Development Container Specification, Atmos manages named container configurations with support for Docker and Podman runtimes.

Experimental

Quick Start

The simplest devcontainer configuration uses a pre-built image:

atmos.yaml

devcontainer:
terraform:
spec:
image: "hashicorp/terraform:1.10"
workspaceFolder: "/workspace"

Launch it with:

atmos devcontainer shell terraform

Configuration Structure

Development containers are configured under the top-level devcontainer key. Each entry is a named configuration that can be launched independently:

atmos.yaml

devcontainer:
<name>: # Named configuration (e.g., "geodesic", "terraform", "python")
settings: # Atmos-specific settings (optional)
runtime: "" # "docker", "podman", or omit for auto-detection
spec: # Development Container specification (required)
# ... Dev Container spec fields ...

Settings

Atmos-specific configuration options that control how containers are managed.

settings.runtime

Specify the container runtime to use: "docker", "podman", or omit for automatic detection.

Default: Auto-detect (checks Docker first, then Podman)

When omitted, Atmos will automatically detect which runtime is available on your system. This is useful for teams where some members use Docker and others use Podman.

Example:

devcontainer:
geodesic:
settings:
runtime: "podman"
spec:
image: "cloudposse/geodesic:latest"

Spec (Development Container Specification)

The spec section follows the Development Container Specification. You can provide either a single map or a list of maps (for merging configurations).

Basic Configuration

spec.name

Display name for the container.

Type: string

Example:

spec:
name: "Terraform Development Environment"
spec.image

Pre-built container image to use. Use this or build, not both.

Type: string

Example:

spec:
image: "hashicorp/terraform:1.10"
spec.build

Build configuration for creating a custom container. Use this or image, not both.

Type: object with fields:

  • dockerfile (string) - Path to Dockerfile
  • context (string) - Build context directory
  • args (map) - Build arguments

Example:

spec:
build:
dockerfile: ".devcontainer/Dockerfile"
context: "."
args:
NODE_VERSION: "20"
PYTHON_VERSION: "3.12"

Workspace Configuration

spec.workspaceFolder

Working directory inside the container where commands will execute.

Type: string Default: /workspace

Example:

spec:
workspaceFolder: "/workspace"
spec.workspaceMount

Primary workspace volume mount specification. Uses Docker mount syntax.

Type: string

Supports variable expansion:

  • ${localWorkspaceFolder} - Path to the workspace on your machine
  • ${localEnv:VAR} - Local environment variable

Example:

spec:
workspaceMount: "type=bind,source=${localWorkspaceFolder},target=/workspace"
spec.mounts

Additional volume mounts beyond the workspace. Common uses include mounting credential directories or SSH keys.

Type: array of strings (Docker mount syntax)

Example:

spec:
mounts:
- "type=bind,source=${localEnv:HOME}/.aws,target=/root/.aws,readonly"
- "type=bind,source=${localEnv:HOME}/.ssh,target=/root/.ssh,readonly"
- "type=volume,source=terraform-cache,target=/root/.terraform.d/plugin-cache"

Port Configuration

spec.forwardPorts

Ports to forward from the container to your host machine. Supports both static ports and the !random YAML function for dynamic port assignment.

Type: array of numbers or strings

Example:

spec:
forwardPorts:
- 3000 # Static port
- !random 8080 8099 # Random port in range (avoids conflicts)
- "8080:8081" # Host 8080 to container 8081

Using !random is recommended when running multiple instances to avoid port conflicts.

spec.portsAttributes

Metadata for forwarded ports, such as labels and protocols.

Type: object mapping port numbers/strings to attribute objects

Each port can have:

  • label (string) - Descriptive label
  • protocol (string) - Protocol (http, https, etc.)

Example:

spec:
portsAttributes:
"3000":
label: "Web Server"
protocol: "http"
"5432":
label: "PostgreSQL"
protocol: "tcp"

Environment Configuration

spec.containerEnv

Environment variables to set inside the container.

Type: object (key-value pairs)

Supports variable expansion:

  • ${localEnv:VAR} - Expand from your local environment
  • ${localWorkspaceFolder} - Path to workspace

Example:

spec:
containerEnv:
AWS_PROFILE: "${localEnv:AWS_PROFILE}"
TF_PLUGIN_CACHE_DIR: "/root/.terraform.d/plugin-cache"
TERM: "${localEnv:TERM}"
spec.remoteUser

User to run as inside the container.

Type: string Default: Depends on the image

Example:

spec:
remoteUser: "root"

Runtime Configuration

spec.runArgs

Additional arguments to pass to docker run or podman run.

Type: array of strings

Example:

spec:
runArgs:
- "--network=host"
- "--dns=8.8.8.8"
spec.overrideCommand

Whether to override the image's ENTRYPOINT/CMD.

Type: boolean Default: false

Example:

spec:
overrideCommand: true
spec.init

Run an init process inside the container to handle signal forwarding and reaping.

Type: boolean Default: false

Example:

spec:
init: true
spec.privileged

Run the container in privileged mode.

Type: boolean Default: false

Warning: Only use when necessary for your workflow.

Example:

spec:
privileged: true
spec.capAdd

Linux capabilities to add to the container.

Type: array of strings

Example:

spec:
capAdd:
- "SYS_PTRACE"
- "NET_ADMIN"
spec.securityOpt

Security options for the container.

Type: array of strings

Example:

spec:
securityOpt:
- "seccomp=unconfined"
spec.userEnvProbe

How to probe the user environment inside the container.

Type: string Options: "none", "loginShell", "loginInteractiveShell", "interactiveShell"

Example:

spec:
userEnvProbe: "loginInteractiveShell"

Advanced Features

Merging Configurations with !include

Reuse existing .devcontainer/devcontainer.json files by merging them with Atmos-specific overrides:

atmos.yaml

devcontainer:
geodesic:
spec:
- !include .devcontainer/devcontainer.json # Base configuration
- forwardPorts: # Override/extend
- !random 8080 8099
containerEnv:
AWS_PROFILE: "dev"

When using a list, configurations are merged in order. Later entries override earlier ones.

Dynamic Port Assignment

Use the !random YAML function to avoid port conflicts when running multiple container instances:

atmos.yaml

devcontainer:
app:
spec:
forwardPorts:
- !random 8080 8099 # Assigns random port in range
- 3000 # Static port

This is especially useful when launching multiple instances with the --instance flag.

Multiple Instances

Run multiple containers from the same configuration using the --instance flag:

# Default instance
atmos devcontainer shell geodesic

# Named instances
atmos devcontainer shell geodesic --instance dev
atmos devcontainer shell geodesic --instance prod
atmos devcontainer shell geodesic --instance testing

Each instance gets a unique container name: atmos-<config>-<instance>.

Common Patterns

Pattern: Simple Image-Based Container

Use a pre-built image for quick development environments:

atmos.yaml

devcontainer:
python:
spec:
image: "python:3.12"
workspaceFolder: "/workspace"
containerEnv:
PYTHONPATH: "/workspace"

Pattern: Custom Dockerfile Build

Build a custom container with specific tools:

atmos.yaml

devcontainer:
custom:
spec:
build:
dockerfile: ".devcontainer/Dockerfile"
context: "."
args:
NODE_VERSION: "20"
TERRAFORM_VERSION: "1.10"
workspaceFolder: "/workspace"

Pattern: Geodesic Shell (Multi-Tool Environment)

Use Geodesic as a comprehensive toolbox:

atmos.yaml

devcontainer:
geodesic:
settings:
runtime: "docker"
spec:
- !include .devcontainer/devcontainer.json
- forwardPorts:
- !random 8080 8099
- !random 3000 3099
containerEnv:
AWS_PROFILE: "dev"
TF_PLUGIN_CACHE_DIR: "/root/.terraform.d/plugin-cache"

Pattern: Mounting Authentication Credentials

Mount cloud provider credentials into your container:

atmos.yaml

devcontainer:
terraform:
spec:
image: "hashicorp/terraform:1.10"
workspaceFolder: "/workspace"
mounts:
- "type=bind,source=${localEnv:HOME}/.aws,target=/root/.aws,readonly"
- "type=bind,source=${localEnv:HOME}/.azure,target=/root/.azure,readonly"
containerEnv:
AWS_PROFILE: "${localEnv:AWS_PROFILE}"
AZURE_SUBSCRIPTION_ID: "${localEnv:AZURE_SUBSCRIPTION_ID}"

Alternatively, use Atmos identity injection with the --identity flag for enhanced security.

Complete Example

A comprehensive configuration showing all major features:

atmos.yaml

devcontainer:
# Simple image-based container
terraform:
spec:
name: "Terraform Development"
image: "hashicorp/terraform:1.10"
workspaceFolder: "/workspace"
forwardPorts:
- !random 8080 8099
mounts:
- "type=bind,source=${localEnv:HOME}/.aws,target=/root/.aws,readonly"
containerEnv:
TF_PLUGIN_CACHE_DIR: "/root/.terraform.d/plugin-cache"
AWS_PROFILE: "${localEnv:AWS_PROFILE}"

# Custom build with Podman
custom:
settings:
runtime: "podman"
spec:
name: "Custom Development Environment"
build:
dockerfile: ".devcontainer/Dockerfile"
context: "."
args:
WORKSPACE: "/workspace"
workspaceFolder: "/workspace"
remoteUser: "developer"
init: true

# Geodesic with merged configuration
geodesic:
spec:
- !include .devcontainer/devcontainer.json
- name: "Geodesic Cloud Shell"
forwardPorts:
- !random 8080 8099
- !random 3000 3099
portsAttributes:
"8080":
label: "Application"
protocol: "http"
containerEnv:
AWS_PROFILE: "dev"
TERM: "${localEnv:TERM}"

Working Examples

Browse complete working examples of devcontainer configurations:

Devcontainer Example

This example demonstrates how to configure devcontainers in Atmos as a replacement for the Geodesic shell wrapper.

Files

  • devcontainer.json - Geodesic devcontainer configuration (VS Code compatible)
  • atmos.yaml - Atmos configuration with multiple devcontainer definitions

Usage

Using the Default Geodesic Devcontainer

# Create and attach to the default devcontainer (Geodesic)
atmos devcontainer start default --attach

# Inside the container
terraform plan
atmos terraform apply vpc -s ue2-dev

Using Named Devcontainers

# Create Terraform-specific devcontainer
atmos devcontainer start terraform --attach

# Create Python devcontainer
atmos devcontainer start python --attach

Using Multiple Instances

# Create multiple instances of the same devcontainer
atmos devcontainer start default --instance alice --attach
atmos devcontainer start default --instance bob --attach

# List all running devcontainers
atmos devcontainer list

Configuration Methods

Method 1: Import existing devcontainer.json

devcontainers:
default: !include devcontainer.json

This imports the VS Code-compatible devcontainer.json file. Unsupported fields (like features, customizations, postCreateCommand) are silently ignored and logged at debug level.

Method 2: Define inline in atmos.yaml

devcontainers:
terraform:
name: "Terraform Dev"
image: "hashicorp/terraform:1.6"
forwardPorts: [8080, 3000]
# ... more configuration

Method 3: Merge imported config with overrides

devcontainers:
custom:
<<: !include devcontainer.json
# Override specific fields
forwardPorts: [9000, 9001]

Supported Fields

  • name - Container display name
  • image - Pre-built container image
  • build - Build configuration (dockerfile, context, args)
  • workspaceFolder - Working directory inside container
  • workspaceMount - Primary workspace volume mount
  • mounts - Additional volume mounts
  • forwardPorts - Port forwarding (critical for dev workflows)
  • portsAttributes - Port metadata (labels, protocols)
  • containerEnv - Environment variables
  • runArgs - Additional docker/podman arguments
  • remoteUser - User to run as

Unsupported Fields (Ignored)

  • features - Use Dockerfile instead
  • customizations - Use VS Code extension instead
  • postCreateCommand / lifecycle scripts - Use Dockerfile ENTRYPOINT/CMD

Port Forwarding Examples

Simple Port Mapping

forwardPorts:
- 8080 # Maps 8080:8080
- 3000 # Maps 3000:3000

Explicit Port Mapping

forwardPorts:
- "8080:8080" # Explicit host:container
- "3001:3000" # Map host 3001 to container 3000

With Port Attributes

forwardPorts:
- 8080

portsAttributes:
"8080":
label: "Web Server"
protocol: "http"

Commands

# Start and attach to devcontainer
atmos devcontainer start <name> --attach

# Start devcontainer without attaching
atmos devcontainer start <name>

# Stop running container
atmos devcontainer stop <name>

# Attach to running container
atmos devcontainer attach <name>

# Execute command in container
atmos devcontainer exec <name> -- terraform plan

# List containers
atmos devcontainer list

# Remove container
atmos devcontainer remove <name>

# Show configuration
atmos devcontainer config <name>

Container Naming

Containers are named: atmos-devcontainer-{name}-{instance}

Examples:

  • atmos-devcontainer-default-default
  • atmos-devcontainer-terraform-alice
  • atmos-devcontainer-python-bob

Custom Dockerfile Devcontainer Example

This example demonstrates using a custom Dockerfile with Atmos devcontainers. The Dockerfile extends the Geodesic base image and pre-installs Atmos.

Files

  • atmos.yaml - Configuration showing how to use devcontainers with Dockerfile builds
  • devcontainer.json - Devcontainer configuration using build instead of image
  • Dockerfile - Custom Dockerfile extending Geodesic with Atmos pre-installed

Configuration

The devcontainer.json uses the build section to specify how to build the container image:

{
"build": {
"dockerfile": "Dockerfile",
"context": ".",
"args": {
"GEODESIC_VERSION": "latest"
}
}
}

Build Arguments

The Dockerfile accepts build arguments for customization:

  • GEODESIC_VERSION - Version of Geodesic to use (default: latest)

Usage

Quick Start

# From this directory, build and launch the devcontainer
cd examples/devcontainer-build
atmos devcontainer shell geodesic

# Force rebuild (when Dockerfile changes)
atmos devcontainer shell geodesic --replace

Using with atmos.yaml

The included atmos.yaml shows two approaches:

Option 1: Include devcontainer.json file

devcontainer:
geodesic:
spec:
- !include devcontainer.json

Option 2: Define build configuration inline

devcontainer:
geodesic-inline:
spec:
name: "Geodesic with Atmos"
build:
dockerfile: "Dockerfile"
context: "."
args:
GEODESIC_VERSION: "latest"
ATMOS_VERSION: "latest"
workspaceFolder: "/workspace"
workspaceMount: "type=bind,source=${localWorkspaceFolder},target=/workspace"
containerEnv:
ATMOS_BASE_PATH: "/workspace"
remoteUser: "root"

Launch the devcontainer

# Build and launch
atmos devcontainer shell geodesic

# Force rebuild (when Dockerfile changes)
atmos devcontainer shell geodesic --replace

# List available devcontainers
atmos devcontainer list

What's Included

Inside the container, you'll have:

  • Geodesic - Full Geodesic shell environment
  • Atmos - Pre-installed Atmos CLI (atmos version)
  • Workspace - Your project mounted at /workspace
  • Environment - ATMOS_BASE_PATH=/workspace pre-configured

Customization

You can customize the Dockerfile to:

  • Install additional tools (terraform, kubectl, helm, etc.)
  • Add custom scripts or configuration
  • Set environment variables
  • Install VS Code extensions
  • Configure shell aliases

Example additions:

# Install additional tools
RUN apk add --no-cache \
terraform \
kubectl \
helm

# Add custom shell configuration
COPY .bashrc /root/.bashrc

# Install VS Code extensions (if using VS Code)
RUN code-server --install-extension hashicorp.terraform

Build Process

When you run atmos devcontainer shell, Atmos will:

  1. Build the image (if not already built or if changed)

    • Uses docker build or podman build
    • Passes build args from devcontainer.json
    • Tags the image as atmos-devcontainer-geodesic
  2. Create the container from the built image

  3. Start and attach to the container

Rebuilding

To rebuild the image after changing the Dockerfile:

# Rebuild the devcontainer
atmos devcontainer rebuild geodesic

# Or use --replace flag with shell command
atmos devcontainer shell geodesic --replace

Benefits of Custom Dockerfiles

  • Reproducibility - Everyone gets the same tools and versions
  • Speed - Tools are pre-installed, no setup time
  • Consistency - Same environment across team members
  • Customization - Add project-specific tools and configuration
  • Version control - Dockerfile is versioned with your project

Environment Variables

The devcontainer configuration is read exclusively from atmos.yaml and does not support environment variable overrides. However, individual container settings support environment variable expansion using ${localEnv:VAR} syntax within the spec section.

See Also