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/repoFrom Gitpod#
Prefix any GitHub URL with gitpod.io/#:
https://gitpod.io/#https://github.com/owner/repoOr use the Gitpod CLI:
gitpod workspace create https://github.com/owner/repoLocally with the Devcontainer CLI#
npm install -g @devcontainers/cli
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . bashFree 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-releaseTemplate 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/dbmatepost-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-docspost-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.mdGitpod 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-yamlChoosing 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.