Docker Setup
Use Docker to create consistent, isolated development environments for Pact smart contract development.
Why Use Docker?
Docker provides several advantages for Pact development:
- Consistent Environment: Same setup across all machines
- Isolated DevNet: Local blockchain that doesn't interfere with other projects
- Easy Cleanup: Tear down and rebuild environments instantly
- Production Parity: Mirror production environments locally
- Team Collaboration: Share exact development setups
Prerequisites
Install Docker
macOS
# Download Docker Desktop
# https://www.docker.com/products/docker-desktop/
# Or using Homebrew
brew install --cask docker
Linux (Ubuntu/Debian)
# Update package index
sudo apt-get update
# Install Docker
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Add user to docker group
sudo usermod -aG docker $USER
# Restart shell or log out/in
newgrp docker
Windows
# Download Docker Desktop
# https://www.docker.com/products/docker-desktop/
# Or using winget
winget install Docker.DockerDesktop
Verify Installation
# Check Docker version
docker --version
# Check Docker Compose
docker compose version
# Test Docker installation
docker run hello-world
Quick Start with Pact Toolbox
Using Docker Compose (Recommended)
Pact Toolbox includes Docker configurations out of the box:
# Create a new project with Docker support
pnpm create pact-toolbox-app my-docker-app --docker
# Navigate to your project
cd my-docker-app
# Start development environment
docker compose up -d
# Your app is now running at http://localhost:3000
# DevNet is available at http://localhost:8080
Manual Docker Setup
If you have an existing project:
# Add Docker support to existing project
pnpm pact-toolbox init --docker
# Or copy example configurations
cp node_modules/@pact-toolbox/config/docker/* .
Docker Configuration Files
docker-compose.yml
version: "3.8"
services:
# Main application
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- KADENA_NETWORK=devnet
- KADENA_API_HOST=http://devnet:8080
depends_on:
- devnet
command: pnpm dev
# Local Kadena DevNet
devnet:
image: kadena/devnet:latest
ports:
- "8080:8080"
environment:
- KADENA_CHAIN_COUNT=1
- KADENA_MINING_ENABLED=true
volumes:
- devnet_data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
# PostgreSQL for data persistence (optional)
db:
image: postgres:15
environment:
- POSTGRES_DB=pact_toolbox
- POSTGRES_USER=pact
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
devnet_data:
postgres_data:
Dockerfile
# Multi-stage build for production
FROM node:18-alpine AS base
# Install pnpm
RUN npm install -g pnpm
# Set working directory
WORKDIR /app
# Copy package files
COPY package.json pnpm-lock.yaml ./
# Development stage
FROM base AS development
RUN pnpm install --frozen-lockfile
COPY . .
EXPOSE 3000
CMD ["pnpm", "dev"]
# Build stage
FROM development AS build
RUN pnpm build
# Production stage
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=base /usr/local/bin/pnpm /usr/local/bin/
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --prod --frozen-lockfile
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["pnpm", "start"]
.dockerignore
# Dependencies
node_modules/
.pnpm-store/
# Build outputs
dist/
build/
.next/
# Environment files
.env
.env.local
.env.*.local
# Logs
*.log
npm-debug.log*
pnpm-debug.log*
# Runtime data
pids/
*.pid
*.seed
*.pid.lock
# Coverage
coverage/
.nyc_output
# IDEs
.vscode/
.idea/
# OS
.DS_Store
Thumbs.db
# Git
.git/
.gitignore
# Docker
Dockerfile*
docker-compose*
.dockerignore
# CI/CD
.github/
Development Workflow
Starting Development
# Start all services
docker compose up -d
# View logs
docker compose logs -f
# Check service status
docker compose ps
Common Commands
# Build containers
docker compose build
# Start specific service
docker compose up devnet
# Stop all services
docker compose down
# Remove volumes (clean slate)
docker compose down -v
# Execute commands in running container
docker compose exec app pnpm test
# Shell into container
docker compose exec app sh
Hot Reloading
The development setup supports hot reloading:
# In docker-compose.yml
services:
app:
volumes:
- .:/app # Mount source code
- /app/node_modules # Exclude node_modules
environment:
- CHOKIDAR_USEPOLLING=true # For Windows
DevNet Configuration
Custom DevNet Setup
# docker-compose.override.yml
services:
devnet:
environment:
# Multiple chains
- KADENA_CHAIN_COUNT=4
# Custom gas settings
- KADENA_GAS_LIMIT=100000
- KADENA_GAS_PRICE=0.00001
# Mining configuration
- KADENA_MINING_ENABLED=true
- KADENA_BLOCK_TIME=5s
# Initial accounts
- KADENA_GENESIS_ACCOUNTS=alice,bob,charlie
ports:
- "8080-8083:8080-8083" # Multiple chain ports
DevNet Data Persistence
# Backup DevNet data
docker compose exec devnet tar -czf /tmp/devnet-backup.tar.gz /data
# Copy backup to host
docker cp $(docker compose ps -q devnet):/tmp/devnet-backup.tar.gz ./devnet-backup.tar.gz
# Restore DevNet data
docker cp ./devnet-backup.tar.gz $(docker compose ps -q devnet):/tmp/
docker compose exec devnet tar -xzf /tmp/devnet-backup.tar.gz -C /
Production Setup
Multi-Stage Builds
# Production-optimized Dockerfile
FROM node:18-alpine AS builder
# Install build dependencies
RUN apk add --no-cache python3 make g++
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Copy source and build
COPY . .
RUN npm run build
# Production image
FROM node:18-alpine AS runtime
WORKDIR /app
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S pactapp -u 1001
# Copy built application
COPY --from=builder --chown=pactapp:nodejs /app/dist ./dist
COPY --from=builder --chown=pactapp:nodejs /app/node_modules ./node_modules
COPY --chown=pactapp:nodejs package.json ./
USER pactapp
EXPOSE 3000
CMD ["node", "dist/index.js"]
Health Checks
# Add health check to Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# In docker-compose.yml
services:
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Networking
Service Communication
Services can communicate using service names:
// In your application
const client = createPactClient({
network: "devnet",
host: "http://devnet:8080", // Use service name
});
External Access
# docker-compose.yml
services:
app:
ports:
- "3000:3000" # App accessible at localhost:3000
devnet:
ports:
- "8080:8080" # DevNet accessible at localhost:8080
Custom Networks
# docker-compose.yml
networks:
pact-network:
driver: bridge
services:
app:
networks:
- pact-network
devnet:
networks:
- pact-network
Environment Variables
.env Files
# .env
NODE_ENV=development
KADENA_NETWORK=devnet
KADENA_API_HOST=http://devnet:8080
DATABASE_URL=postgres://pact:password@db:5432/pact_toolbox
Docker Compose Variables
# docker-compose.yml
services:
app:
env_file:
- .env
- .env.local
environment:
- PORT=3000
- DEBUG=pact-toolbox:*
Volume Management
Types of Volumes
services:
app:
volumes:
# Named volume for data persistence
- app_data:/app/data
# Bind mount for development
- .:/app
# Anonymous volume for node_modules
- /app/node_modules
volumes:
app_data:
Backup and Restore
# Backup named volume
docker run --rm -v pact_devnet_data:/data -v $(pwd):/backup alpine tar czf /backup/devnet-backup.tar.gz /data
# Restore named volume
docker run --rm -v pact_devnet_data:/data -v $(pwd):/backup alpine tar xzf /backup/devnet-backup.tar.gz -C /
Performance Optimization
Build Performance
# Use build cache
FROM node:18-alpine
WORKDIR /app
# Copy package files first for better caching
COPY package*.json ./
RUN npm ci --only=production
# Copy source last
COPY . .
RUN npm run build
Runtime Performance
# docker-compose.yml
services:
app:
deploy:
resources:
limits:
cpus: "1.0"
memory: 1G
reservations:
cpus: "0.5"
memory: 512M
Troubleshooting
Common Issues
Port Already in Use
# Find process using port
lsof -i :8080
# Kill process
kill -9 <PID>
# Or use different port
docker compose up --port 8081:8080
Permission Denied
# Fix file permissions
sudo chown -R $USER:$USER .
# Or run with sudo (not recommended)
sudo docker compose up
Container Won't Start
# Check logs
docker compose logs app
# Debug container
docker compose run --rm app sh
# Check container status
docker compose ps
Performance Issues
# Monitor resource usage
docker stats
# Check disk usage
docker system df
# Clean up unused resources
docker system prune -a
CI/CD Integration
GitHub Actions
# .github/workflows/docker.yml
name: Docker Build and Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t pact-app .
- name: Start services
run: docker compose up -d
- name: Run tests
run: docker compose exec -T app pnpm test
- name: Stop services
run: docker compose down -v
Production Deployment
# docker-compose.prod.yml
version: "3.8"
services:
app:
image: your-registry/pact-app:latest
environment:
- NODE_ENV=production
- KADENA_NETWORK=mainnet
deploy:
replicas: 3
restart_policy:
condition: on-failure
Best Practices
1. Use Multi-Stage Builds
Separate build and runtime environments for smaller production images.
2. Leverage Build Cache
Structure Dockerfiles to maximize layer caching.
3. Use .dockerignore
Exclude unnecessary files to speed up builds.
4. Health Checks
Always include health checks for production services.
5. Resource Limits
Set appropriate CPU and memory limits.
6. Security
- Use non-root users
- Scan images for vulnerabilities
- Keep base images updated