Devcontainer Sandbox Templates#

Devcontainers provide disposable, reproducible development environments that run in a container. You define the tools, extensions, and configuration in a .devcontainer/ directory, and any compatible host – GitHub Codespaces, Gitpod, VS Code with Docker, or the devcontainer CLI – builds and launches the environment from that definition.

For infrastructure validation, devcontainers solve a specific problem: giving every developer and every CI run the exact same set of tools at the exact same versions, without requiring them to install anything on their local machine. A Kubernetes devcontainer includes kind, kubectl, helm, and kustomize at pinned versions. A Terraform devcontainer includes terraform, tflint, checkov, and cloud CLIs. The environment is ready to use the moment it starts.

How to Launch#

From GitHub (Codespaces)#

The fastest path: navigate to a GitHub repository that contains a .devcontainer/ directory, click the green “Code” button, select the “Codespaces” tab, and click “Create codespace on main.” The environment builds and opens in a browser-based VS Code editor.

From the CLI:

gh codespace create --repo owner/repo --branch main --machine basicLinuxCodespace
gh codespace ssh --repo owner/repo

From Gitpod#

Prefix any GitHub URL with gitpod.io/#:

https://gitpod.io/#https://github.com/owner/repo

Or use the Gitpod CLI:

gitpod workspace create https://github.com/owner/repo

Locally with the Devcontainer CLI#

npm install -g @devcontainers/cli
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . bash

Free Tier Limits#

GitHub Codespaces free tier (as of 2025): 120 core-hours per month for personal accounts. A 2-core machine gives you 60 hours of usage. A 4-core machine gives 30 hours. For infrastructure validation where you spin up a codespace, run tests, and close it, 120 core-hours goes far.

Gitpod free tier: 50 hours per month on standard workspaces (4 cores, 8GB RAM). Workspaces auto-stop after 30 minutes of inactivity.

To stay within free tiers: always stop codespaces when not in use (gh codespace stop), use 2-core machines when possible, and set auto-shutdown timeouts.

Template 1: Kubernetes Development#

This template includes kind (Kubernetes in Docker), kubectl, helm, kustomize, and common debugging tools. It creates a kind cluster automatically on container start.

devcontainer.json#

{
  "name": "Kubernetes Development",
  "build": {
    "dockerfile": "Dockerfile"
  },
  "features": {
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  },
  "postCreateCommand": ".devcontainer/post-create.sh",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-kubernetes-tools.vscode-kubernetes-tools",
        "redhat.vscode-yaml",
        "tim-koehler.helm-intellisense"
      ]
    }
  },
  "forwardPorts": [8080, 8443],
  "remoteUser": "vscode"
}

Dockerfile#

FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04

ARG KIND_VERSION=0.24.0
ARG KUBECTL_VERSION=1.29.3
ARG HELM_VERSION=3.14.3
ARG KUSTOMIZE_VERSION=5.3.0

# kubectl
RUN curl -fsSL "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl" \
      -o /usr/local/bin/kubectl \
    && chmod +x /usr/local/bin/kubectl

# kind
RUN curl -fsSL "https://kind.sigs.k8s.io/dl/v${KIND_VERSION}/kind-linux-amd64" \
      -o /usr/local/bin/kind \
    && chmod +x /usr/local/bin/kind

# helm
RUN curl -fsSL "https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz" \
    | tar xz -C /usr/local/bin --strip-components=1 linux-amd64/helm

# kustomize
RUN curl -fsSL "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv${KUSTOMIZE_VERSION}/kustomize_v${KUSTOMIZE_VERSION}_linux_amd64.tar.gz" \
    | tar xz -C /usr/local/bin

# kubeval alternative: kubeconform
RUN curl -fsSL "https://github.com/yannh/kubeconform/releases/latest/download/kubeconform-linux-amd64.tar.gz" \
    | tar xz -C /usr/local/bin

# helm-diff plugin and debugging tools
RUN apt-get update && apt-get install -y --no-install-recommends \
      jq yq dnsutils iputils-ping curl netcat-openbsd \
    && rm -rf /var/lib/apt/lists/*

post-create.sh#

#!/bin/bash
set -euo pipefail

echo "Creating kind cluster..."
kind create cluster --name dev --wait 120s

echo "Verifying cluster..."
kubectl cluster-info
kubectl get nodes

echo "Installing helm diff plugin..."
helm plugin install https://github.com/databus23/helm-diff || true

echo "Kubernetes devcontainer ready."
echo "  kubectl:    $(kubectl version --client --short 2>/dev/null || kubectl version --client)"
echo "  helm:       $(helm version --short)"
echo "  kind:       $(kind version)"
echo "  kustomize:  $(kustomize version --short 2>/dev/null || kustomize version)"

Usage#

After the devcontainer starts, you have a running kind cluster. Validate Helm charts directly:

# Lint a chart
helm lint ./my-chart/

# Template and validate
helm template my-release ./my-chart/ | kubeconform -strict -

# Install and test
helm install my-release ./my-chart/ --wait --timeout 120s
helm test my-release
helm uninstall my-release

Template 2: Database Development#

This template runs PostgreSQL, MySQL, and Redis locally with migration tools pre-installed. Useful for validating schema migrations, testing connection configurations, and developing database-backed applications.

devcontainer.json#

{
  "name": "Database Development",
  "dockerComposeFile": "docker-compose.yml",
  "service": "workspace",
  "workspaceFolder": "/workspace",
  "postCreateCommand": ".devcontainer/post-create.sh",
  "customizations": {
    "vscode": {
      "extensions": [
        "mtxr.sqltools",
        "mtxr.sqltools-driver-pg",
        "mtxr.sqltools-driver-mysql",
        "redhat.vscode-yaml"
      ]
    }
  },
  "forwardPorts": [5432, 3306, 6379],
  "remoteUser": "vscode"
}

docker-compose.yml#

services:
  workspace:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ..:/workspace:cached
    command: sleep infinity
    depends_on:
      postgres:
        condition: service_healthy
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: devpass
      POSTGRES_DB: devdb
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dev"]
      interval: 5s
      retries: 10
    ports:
      - "5432:5432"

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_USER: dev
      MYSQL_PASSWORD: devpass
      MYSQL_DATABASE: devdb
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-prootpass"]
      interval: 5s
      retries: 10
    ports:
      - "3306:3306"

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      retries: 10
    ports:
      - "6379:6379"

Dockerfile#

FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04

# PostgreSQL client
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
      postgresql-client \
      mysql-client \
      redis-tools \
      jq \
    && rm -rf /var/lib/apt/lists/*

# golang-migrate for schema migrations
ARG MIGRATE_VERSION=4.17.0
RUN curl -fsSL "https://github.com/golang-migrate/migrate/releases/download/v${MIGRATE_VERSION}/migrate.linux-amd64.tar.gz" \
    | tar xz -C /usr/local/bin

# dbmate as an alternative migration tool
RUN curl -fsSL "https://github.com/amacneil/dbmate/releases/latest/download/dbmate-linux-amd64" \
      -o /usr/local/bin/dbmate \
    && chmod +x /usr/local/bin/dbmate

post-create.sh#

#!/bin/bash
set -euo pipefail

echo "Verifying database connections..."
pg_isready -h postgres -U dev
mysqladmin ping -h mysql -u root -prootpass --silent
redis-cli -h redis ping

echo "Database devcontainer ready."
echo "  PostgreSQL: postgres://dev:devpass@postgres:5432/devdb"
echo "  MySQL:      mysql://dev:devpass@mysql:3306/devdb"
echo "  Redis:      redis://redis:6379"
echo ""
echo "Migration tools:"
echo "  migrate:  $(migrate -version 2>&1 || echo 'installed')"
echo "  dbmate:   $(dbmate --version)"

Template 3: Full-Stack Infrastructure#

This template combines a kind cluster with databases and a basic observability stack (Prometheus and Grafana). It validates the complete application infrastructure locally.

devcontainer.json#

{
  "name": "Full-Stack Infrastructure",
  "build": {
    "dockerfile": "Dockerfile"
  },
  "features": {
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  },
  "postCreateCommand": ".devcontainer/post-create.sh",
  "customizations": {
    "vscode": {
      "extensions": [
        "ms-kubernetes-tools.vscode-kubernetes-tools",
        "redhat.vscode-yaml",
        "tim-koehler.helm-intellisense"
      ]
    }
  },
  "forwardPorts": [3000, 9090, 8080],
  "hostRequirements": {
    "cpus": 4,
    "memory": "8gb"
  },
  "remoteUser": "vscode"
}

Dockerfile#

FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04

ARG KIND_VERSION=0.24.0
ARG KUBECTL_VERSION=1.29.3
ARG HELM_VERSION=3.14.3

RUN curl -fsSL "https://dl.k8s.io/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl" \
      -o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl \
    && curl -fsSL "https://kind.sigs.k8s.io/dl/v${KIND_VERSION}/kind-linux-amd64" \
      -o /usr/local/bin/kind && chmod +x /usr/local/bin/kind \
    && curl -fsSL "https://get.helm.sh/helm-v${HELM_VERSION}-linux-amd64.tar.gz" \
      | tar xz -C /usr/local/bin --strip-components=1 linux-amd64/helm

RUN apt-get update && apt-get install -y --no-install-recommends \
      postgresql-client jq \
    && rm -rf /var/lib/apt/lists/*

post-create.sh#

#!/bin/bash
set -euo pipefail

echo "Creating kind cluster with port mappings..."
cat <<EOF | kind create cluster --name infra --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 30080
        hostPort: 8080
      - containerPort: 30090
        hostPort: 9090
      - containerPort: 30030
        hostPort: 3000
  - role: worker
  - role: worker
EOF

echo "Installing observability stack..."
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

helm install prometheus prometheus-community/prometheus \
  --namespace monitoring --create-namespace \
  --set server.service.type=NodePort \
  --set server.service.nodePort=30090 \
  --set alertmanager.enabled=false \
  --wait --timeout 300s

helm install grafana grafana/grafana \
  --namespace monitoring \
  --set service.type=NodePort \
  --set service.nodePort=30030 \
  --set adminPassword=admin \
  --wait --timeout 300s

echo "Installing databases in-cluster..."
helm install postgresql oci://registry-1.docker.io/bitnamicharts/postgresql \
  --namespace databases --create-namespace \
  --set auth.postgresPassword=devpass \
  --set auth.database=devdb \
  --wait --timeout 300s

echo "Full-stack infrastructure devcontainer ready."
echo "  Kubernetes:  kind cluster 'infra' (3 nodes)"
echo "  Prometheus:  http://localhost:9090"
echo "  Grafana:     http://localhost:3000 (admin/admin)"
echo "  PostgreSQL:  kubectl port-forward -n databases svc/postgresql 5432:5432"

Note the hostRequirements in devcontainer.json. This template needs a 4-core, 8GB machine. On GitHub Codespaces, this uses a 4-core machine type, consuming 4 core-hours per hour of use. Plan your free tier budget accordingly.

Template 4: Terraform Development#

This template includes Terraform, all three major cloud CLIs, and static analysis tools. It does not provision cloud resources – it validates Terraform configurations locally.

devcontainer.json#

{
  "name": "Terraform Development",
  "build": {
    "dockerfile": "Dockerfile"
  },
  "postCreateCommand": ".devcontainer/post-create.sh",
  "customizations": {
    "vscode": {
      "extensions": [
        "hashicorp.terraform",
        "redhat.vscode-yaml"
      ]
    }
  },
  "remoteUser": "vscode"
}

Dockerfile#

FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04

ARG TERRAFORM_VERSION=1.7.5
ARG TFLINT_VERSION=0.50.3

# Terraform
RUN curl -fsSL "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip" \
      -o /tmp/terraform.zip \
    && unzip /tmp/terraform.zip -d /usr/local/bin \
    && rm /tmp/terraform.zip

# tflint
RUN curl -fsSL "https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/tflint_linux_amd64.zip" \
      -o /tmp/tflint.zip \
    && unzip /tmp/tflint.zip -d /usr/local/bin \
    && rm /tmp/tflint.zip

# checkov
RUN apt-get update && apt-get install -y --no-install-recommends python3 python3-pip \
    && pip3 install --no-cache-dir checkov \
    && rm -rf /var/lib/apt/lists/*

# AWS CLI
RUN curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o /tmp/aws.zip \
    && unzip /tmp/aws.zip -d /tmp \
    && /tmp/aws/install \
    && rm -rf /tmp/aws /tmp/aws.zip

# Google Cloud CLI
RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" \
      > /etc/apt/sources.list.d/google-cloud-sdk.list \
    && curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \
      | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg \
    && apt-get update && apt-get install -y --no-install-recommends google-cloud-cli \
    && rm -rf /var/lib/apt/lists/*

# Azure CLI
RUN curl -fsSL https://aka.ms/InstallAzureCLIDeb | bash

# terraform-docs
RUN curl -fsSL "https://github.com/terraform-docs/terraform-docs/releases/latest/download/terraform-docs-v0.18.0-linux-amd64.tar.gz" \
    | tar xz -C /usr/local/bin terraform-docs

post-create.sh#

#!/bin/bash
set -euo pipefail

echo "Initializing tflint plugins..."
cat > /home/vscode/.tflint.hcl <<'EOF'
plugin "aws" {
  enabled = true
  version = "0.30.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}
plugin "google" {
  enabled = true
  version = "0.27.1"
  source  = "github.com/terraform-linters/tflint-ruleset-google"
}
plugin "azurerm" {
  enabled = true
  version = "0.26.0"
  source  = "github.com/terraform-linters/tflint-ruleset-azurerm"
}
EOF
tflint --init

echo "Terraform devcontainer ready."
echo "  terraform:      $(terraform version -json | jq -r '.terraform_version')"
echo "  tflint:         $(tflint --version)"
echo "  checkov:        $(checkov --version 2>&1)"
echo "  aws:            $(aws --version)"
echo "  gcloud:         $(gcloud version 2>&1 | head -1)"
echo "  az:             $(az version --output tsv 2>/dev/null | head -1)"
echo "  terraform-docs: $(terraform-docs version)"

Usage#

# Validate Terraform configuration
cd /workspace/terraform
terraform init -backend=false
terraform validate
tflint --recursive
checkov -d . --framework terraform

# Generate documentation
terraform-docs markdown table ./modules/my-module > ./modules/my-module/README.md

Gitpod Configuration#

For teams using Gitpod instead of Codespaces, add a .gitpod.yml at the repository root. The devcontainer Dockerfile works as-is:

image:
  file: .devcontainer/Dockerfile

tasks:
  - name: Setup
    init: bash .devcontainer/post-create.sh
    command: echo "Environment ready"

ports:
  - port: 8080
    onOpen: ignore
  - port: 9090
    onOpen: ignore
  - port: 3000
    onOpen: ignore

vscode:
  extensions:
    - ms-kubernetes-tools.vscode-kubernetes-tools
    - hashicorp.terraform
    - redhat.vscode-yaml

Choosing a Template#

Template Tools Machine Size Use Case
Kubernetes kind, kubectl, helm, kustomize, kubeconform 2-core, 4GB Helm chart validation, manifest testing, K8s integration tests
Database PostgreSQL, MySQL, Redis, migrate, dbmate 2-core, 4GB Schema migration testing, query validation, connection config
Full-Stack kind + databases + Prometheus + Grafana 4-core, 8GB End-to-end infrastructure testing, observability pipeline validation
Terraform terraform, tflint, checkov, cloud CLIs 2-core, 4GB IaC linting, policy checking, plan validation (no apply)

For most infrastructure validation, start with the smallest template that covers your needs. The Kubernetes template on a 2-core Codespace gives you 60 hours per month on the free tier – more than enough for daily validation cycles.