ANTIGRAVITY LABJP
Articles/Tips & Best Practices
Tips & Best Practices/2026-03-28Advanced

Antigravity × Dev Containers: Building Fully Reproducible AI Development Environments with Docker

A hands-on guide to creating fully reproducible AI development environments using Dev Containers and Docker with Antigravity. Covers devcontainer.json design patterns, multi-stage builds, and team-wide environment sharing.

antigravity374docker2dev-containersdevcontainer2development-environmentreproducibility2team-development

Setup and context — Eliminating "Works on My Machine" Forever

In AI-driven development, environment reproducibility directly impacts productivity. The code generated by Antigravity's agents depends heavily on the execution environment — Node.js versions, Python packages, system libraries, and more. The classic "works on my machine" problem between team members can devastate development velocity.

This article walks you through using Dev Containers and Docker to make your Antigravity AI development environment fully reproducible. From devcontainer.json design patterns to multi-stage builds and CI/CD integration, you'll build a production-ready setup step by step.

Target audience: Engineers familiar with Docker basics (docker build, docker run) who want to streamline team development with Antigravity

Prerequisites:

  • Docker Desktop 4.x or later
  • Antigravity IDE (latest version)
  • Node.js 22 LTS / Python 3.12+ (inside the container)

What Are Dev Containers — IDE and Container Integration Architecture

Dev Containers is an open specification for defining and sharing development environments as containers. By placing a .devcontainer/devcontainer.json file at the root of your project, the IDE automatically starts a container and operates within it.

Traditional environment setup relied on README instructions like "install Node.js 22" or "Python 3.12 required," but actual version mismatches and missing system libraries caused constant issues. Dev Containers solve this problem at its root.

Antigravity natively supports Dev Containers. When it detects a .devcontainer/devcontainer.json, AI agents automatically operate within the container. This ensures that agent-generated code is always executed and tested in a unified environment.

Core Components of Dev Containers

A Dev Container setup consists of three main elements. First, devcontainer.json is the central configuration file that defines container settings, extensions, and port forwarding. Second, a Dockerfile (or docker-compose.yml) defines the base image, packages, and system configuration. Third, Features are reusable development tool packages that make it easy to add Node.js, Python, Docker-in-Docker, and more.


Basic Setup — Designing devcontainer.json for Antigravity

Let's start by designing a basic devcontainer.json optimized for Antigravity development.

// .devcontainer/devcontainer.json
{
  "name": "Antigravity AI Dev",
  "build": {
    "dockerfile": "Dockerfile",
    "context": "..",
    "args": {
      "NODE_VERSION": "22",
      "PYTHON_VERSION": "3.12"
    }
  },
  "features": {
    "ghcr.io/devcontainers/features/git:1": {},
    "ghcr.io/devcontainers/features/github-cli:1": {},
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  },
  "customizations": {
    "antigravity": {
      "extensions": [],
      "settings": {
        "terminal.integrated.defaultProfile.linux": "zsh"
      }
    }
  },
  "forwardPorts": [3000, 5432, 6379],
  "postCreateCommand": "npm install && npm run setup",
  "postStartCommand": "echo '✅ Dev Container ready'",
  "remoteUser": "node"
}

In this configuration, the build section uses a custom Dockerfile with Node.js and Python versions controlled through build arguments. The features section adds Git, GitHub CLI, and Docker-in-Docker, while forwardPorts automatically forwards development server and database ports.


Custom Dockerfile — Optimizing with Multi-Stage Builds

While basic Dev Containers can be built with Features alone, reproducing a production-like environment requires a custom Dockerfile. Multi-stage builds eliminate unnecessary layers, optimizing build time and image size.

# .devcontainer/Dockerfile
# ============================================
# Stage 1: Base image + system dependencies
# ============================================
ARG NODE_VERSION=22
FROM mcr.microsoft.com/devcontainers/javascript-node:${NODE_VERSION} AS base
 
ARG PYTHON_VERSION=3.12
RUN apt-get update && apt-get install -y --no-install-recommends \
    python${PYTHON_VERSION} \
    python${PYTHON_VERSION}-venv \
    python3-pip \
    build-essential \
    libcairo2-dev \
    libpango1.0-dev \
    libjpeg-dev \
    libgif-dev \
    librsvg2-dev \
    && rm -rf /var/lib/apt/lists/*
 
# Create Python virtual environment
RUN python${PYTHON_VERSION} -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
 
# ============================================
# Stage 2: Dependency installation
# ============================================
FROM base AS deps
 
WORKDIR /workspace
COPY package.json package-lock.json ./
RUN npm ci --prefer-offline
 
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
 
# ============================================
# Stage 3: Development environment (final image)
# ============================================
FROM base AS development
 
# Copy node_modules from deps stage
COPY --from=deps /workspace/node_modules /workspace/node_modules
COPY --from=deps /opt/venv /opt/venv
 
# Set up zsh + oh-my-zsh
RUN apt-get update && apt-get install -y zsh \
    && sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended \
    && rm -rf /var/lib/apt/lists/*
 
# Developer convenience tools
RUN npm install -g \
    typescript \
    tsx \
    @biomejs/biome \
    turbo
 
WORKDIR /workspace
ENV NODE_ENV=development

There are three key points to this multi-stage build. Stage 1 locks system dependencies and uses --no-install-recommends to exclude unnecessary packages. Stage 2 copies only package.json and requirements.txt to maximize cache layer hits. Stage 3 copies only the artifacts from the deps stage and adds development tools.

With this setup, source code changes only trigger a rebuild of Stage 3, while dependency installation leverages cache, dramatically reducing build times after the initial run.


Secure Environment Variable and Secret Management

Development environments deal with API keys, database connection strings, and other secrets that must never be hardcoded in devcontainer.json or Dockerfiles.

// .devcontainer/devcontainer.json (excerpt)
{
  "containerEnv": {
    "NODE_ENV": "development",
    "LOG_LEVEL": "debug",
    "API_BASE_URL": "http://localhost:3000"
  },
  "secrets": {
    "DATABASE_URL": {
      "description": "PostgreSQL connection string",
      "documentationUrl": "https://wiki.example.com/db-setup"
    },
    "STRIPE_SECRET_KEY": {
      "description": "Stripe API secret key (test mode)"
    }
  }
}

Non-sensitive environment variables go directly in containerEnv, while the secrets section prompts the IDE to ask users for secure input. This mechanism eliminates the risk of secrets being committed to the Git repository.

Another practical pattern is including a .env.example file in the repository and copying it to a local .env in the postCreateCommand.

{
  "postCreateCommand": "cp -n .env.example .env && npm install"
}

The -n flag prevents overwriting an existing .env, protecting individual settings.


Antigravity Agent Integration — Connecting AGENTS.md with Containers

Antigravity's agents automatically detect the container environment when running inside a Dev Container. Adding Dev Container-specific instructions to AGENTS.md optimizes agent behavior.

<!-- AGENTS.md -->
# Development Environment
 
This project runs in a Dev Container.
 
## Environment Constraints
- Node.js 22 LTS (pre-installed in container)
- Python 3.12 (installed in /opt/venv)
- PostgreSQL accessible on port 5432 (started via docker-compose)
- Redis accessible on port 6379
 
## Command Execution Rules
- Run `npm` commands in the /workspace directory
- Run Python scripts within the virtual environment (/opt/venv)
- Use `npm run db:migrate` for database migrations
- Use `npm run test` to run the full test suite
 
## Prohibited Actions
- Do not install npm packages globally (use node_modules only)
- Do not add system packages via apt (modify the Dockerfile instead)
- Do not edit .env directly (use .env.local)

With this AGENTS.md, Antigravity's agents understand the container environment constraints and generate code accordingly. For example, when adding a Python package, the agent will suggest adding it to requirements.txt and rebuilding the Dockerfile, rather than running pip install directly.


Docker Compose for Multi-Service Architectures

Real-world projects need more than just an application server — databases, caches, and message queues are common requirements. Integrating docker-compose.yml with Dev Containers launches a complete development stack in one go.

# docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: .devcontainer/Dockerfile
      target: development
    volumes:
      - .:/workspace:cached
      - node_modules:/workspace/node_modules
    ports:
      - "3000:3000"
      - "9229:9229"  # Node.js debug port
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      DATABASE_URL: postgres://dev:devpass@postgres:5432/appdb
      REDIS_URL: redis://redis:6379
      NODE_ENV: development
 
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: devpass
      POSTGRES_DB: appdb
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dev"]
      interval: 5s
      timeout: 5s
      retries: 5
 
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/var/lib/redis/data
 
volumes:
  node_modules:
  postgres_data:
  redis_data:

Here's the devcontainer.json configuration for referencing Docker Compose:

// .devcontainer/devcontainer.json (Docker Compose version)
{
  "name": "Antigravity Full Stack",
  "dockerComposeFile": ["../docker-compose.yml"],
  "service": "app",
  "workspaceFolder": "/workspace",
  "features": {
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },
  "forwardPorts": [3000, 5432, 6379],
  "postCreateCommand": "npm install && npm run db:migrate",
  "remoteUser": "node"
}

The key here is volume design. Making node_modules a named volume avoids filesystem differences between host and container (especially macOS file-watching overhead) and can dramatically speed up npm install.


Performance Optimization — Improving Build Times and I/O

The biggest factor in Dev Container developer experience is performance. Applying these optimization techniques can dramatically improve container startup times and file I/O performance.

Volume Mount Optimization

On macOS and Windows, file synchronization between host and container becomes a bottleneck. Use the cached mount option and separate heavy directories into named volumes.

{
  "mounts": [
    "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached",
    "source=antigravity-node-modules,target=/workspace/node_modules,type=volume",
    "source=antigravity-next-cache,target=/workspace/.next,type=volume"
  ]
}

Separating node_modules and .next (Next.js build cache) into volumes can improve file-watching performance by up to 10x on macOS.

Leveraging BuildKit Cache

Using BuildKit cache mounts in Dockerfiles allows reusing downloaded packages when re-running npm install or pip install.

# High-speed installation with BuildKit cache
RUN --mount=type=cache,target=/root/.npm \
    npm ci --prefer-offline
 
RUN --mount=type=cache,target=/root/.cache/pip \
    pip install --no-cache-dir -r requirements.txt

Prebuilds for Instant Startup

When using GitHub Codespaces or Antigravity Cloud, configuring prebuilds can reduce initial container startup time to near zero.

// .devcontainer/devcontainer.json
{
  "updateContentCommand": "npm ci && npm run build",
  "waitFor": "updateContentCommand"
}

updateContentCommand runs during prebuild and is cached before postCreateCommand. This means dependency installation is skipped when creating new branches or rebuilding containers.


CI/CD Integration — Bridging Development and Production Environments

Using the same Dev Container definition in CI/CD pipelines eliminates the "passes in CI but fails locally" problem.

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
 
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Build Dev Container
        uses: devcontainers/ci@v0.3
        with:
          runCmd: |
            npm run lint
            npm run typecheck
            npm run test -- --coverage
            npm run build
 
      - name: Upload coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage/

The devcontainers/ci action reads .devcontainer/devcontainer.json, builds the container, and runs commands inside it. This guarantees that local development, CI, and staging environments all share the same container definition.

GitHub Actions Cache Strategy

To reduce CI build times, leverage Docker layer caching.

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
 
      - name: Cache Docker layers
        uses: actions/cache@v4
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ hashFiles('.devcontainer/**') }}
          restore-keys: |
            ${{ runner.os }}-buildx-

Team Development Best Practices

When running Dev Containers across a team, establishing these rules maintains environment consistency.

1. Mandatory PR Reviews for Dockerfile Changes

Set up a CODEOWNERS file to require reviews for Dev Container-related files.

# .github/CODEOWNERS
.devcontainer/ @team-lead @devops
Dockerfile     @team-lead @devops
docker-compose.yml @team-lead @devops

2. Environment Diff Detection Script

Provide a script that verifies whether a team member's container environment is up to date.

#!/bin/bash
# scripts/verify-env.sh
# Container environment integrity check
 
echo "🔍 Starting environment verification..."
 
# Node.js version
NODE_EXPECTED="v22"
NODE_ACTUAL=$(node -v | cut -d. -f1)
if [ "$NODE_ACTUAL" != "$NODE_EXPECTED" ]; then
  echo "❌ Node.js: expected $NODE_EXPECTED, got $NODE_ACTUAL"
  exit 1
fi
 
# Python version
PYTHON_EXPECTED="3.12"
PYTHON_ACTUAL=$(python3 --version | cut -d' ' -f2 | cut -d. -f1,2)
if [ "$PYTHON_ACTUAL" != "$PYTHON_EXPECTED" ]; then
  echo "❌ Python: expected $PYTHON_EXPECTED, got $PYTHON_ACTUAL"
  exit 1
fi
 
# Required command checks
for cmd in git gh docker npm npx tsx biome; do
  if ! command -v $cmd &> /dev/null; then
    echo "❌ Missing command: $cmd"
    exit 1
  fi
done
 
echo "✅ Environment verification passed"

3. Auto-Update Notifications

When the Dev Container definition changes, prompt team members to rebuild using postStartCommand.

{
  "postStartCommand": "bash .devcontainer/check-rebuild.sh"
}
#!/bin/bash
# .devcontainer/check-rebuild.sh
HASH_FILE="/tmp/.devcontainer-hash"
CURRENT_HASH=$(md5sum .devcontainer/Dockerfile .devcontainer/devcontainer.json 2>/dev/null | md5sum | cut -d' ' -f1)
 
if [ -f "$HASH_FILE" ]; then
  STORED_HASH=$(cat "$HASH_FILE")
  if [ "$CURRENT_HASH" != "$STORED_HASH" ]; then
    echo ""
    echo "⚠️  Dev Container definition has been updated."
    echo "   Rebuilding is recommended: Cmd+Shift+P → 'Rebuild Container'"
    echo ""
  fi
fi
 
echo "$CURRENT_HASH" > "$HASH_FILE"

Troubleshooting — Common Issues and Solutions

Here are the most common problems you'll encounter when running Dev Containers, along with their solutions.

Slow Container Startup

The most common cause is volume mount I/O. Verify that consistency=cached is configured and that node_modules is separated into a named volume. If it's still slow, increase memory and CPU allocation in Docker Desktop settings.

Port Forwarding Not Working

Check whether the ports specified in forwardPorts are already in use by other processes. Use lsof -i :3000 to check port usage and change the port number if there's a conflict.

File Permission Issues

When host and container user IDs differ, file read/write operations throw permission errors. Set remoteUser correctly and use chown in the Dockerfile as needed.

# Set workspace ownership to the development user
RUN mkdir -p /workspace && chown -R node:node /workspace

Summary

By leveraging Dev Containers and Docker, you can make your Antigravity AI development environment fully reproducible, dramatically boosting your entire team's productivity. Combining devcontainer.json design, multi-stage build optimization, CI/CD integration, and team operation best practices eliminates one of development teams' biggest challenges: environment-related bugs.

Start with a basic devcontainer.json and gradually enrich the configuration as your project grows. Once built, onboarding new team members becomes as simple as "clone the repo and start the Dev Container."

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Integrations2026-04-26
Build a Reproducible Antigravity Development Environment With DevContainers
Pair Antigravity with DevContainers and your whole team gets a one-click reproducible environment. Here is the configuration shape, the agent integration tweaks, and the friction points I ran into.
Tips2026-06-17
Three Prompts I Tried When Antigravity's Code Felt Correct But Not Mine
When Antigravity's output runs but never quite fits your codebase, the gap is usually missing design context. Three prompting patterns for handing over intent — plus the cases where even that wasn't enough, from real indie development.
Tips2026-06-16
Measuring the Go-Based Antigravity CLI's Responsiveness to Rethink My Nightly Batch
The Antigravity CLI was reimplemented in Go, and startup and first-response feel different now. I measure startup, time-to-first-token, and throughput as three separate intervals, then use those numbers to move my nightly batch from serial to parallel.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →