Docker has revolutionized how we package and deploy applications. Combined with preview environments, you can ensure that what works in development works exactly the same way in your preview—and eventually in production.

Why Docker for Preview Environments?

Docker provides several advantages for preview environments:

  • Consistency: Same container runs everywhere
  • Isolation: Dependencies are bundled together
  • Flexibility: Run any stack, any language
  • Speed: Container images are cached and reused

Basic Docker Deployment

If your project has a Dockerfile, prev automatically detects and builds it:

prev
# Automatically detects Dockerfile and builds the image

Sample Dockerfile

Here's a basic Dockerfile for a Node.js application:

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

Deploying Pre-built Images

Already have an image in a registry? Deploy it directly:

prev --image myregistry.io/myapp:latest

Private Registry Authentication

For private registries, configure authentication:

prev --image registry.gitlab.com/team/app:v1.2 \
     --registry-user $REGISTRY_USER \
     --registry-password $REGISTRY_PASSWORD

Multi-Container Applications

For applications with multiple services (API, database, cache), use Docker Compose:

docker-compose.yml

version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://db:5432/app
      - REDIS_URL=redis://cache:6379
    depends_on:
      - db
      - cache

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=app
      - POSTGRES_PASSWORD=secret
    volumes:
      - pgdata:/var/lib/postgresql/data

  cache:
    image: redis:7-alpine

volumes:
  pgdata:

Deploy with Compose

prev --compose

Environment Variables

Pass environment-specific variables to your containers:

prev --env NODE_ENV=preview \
     --env API_KEY=$STAGING_API_KEY \
     --env LOG_LEVEL=debug

Using .env Files

For multiple variables, use an env file:

prev --env-file .env.preview

Build Arguments

Pass build-time arguments to your Dockerfile:

prev --build-arg VERSION=1.2.3 \
     --build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)

Resource Configuration

Specify resource limits for your preview:

prev --memory 512m --cpu 0.5

Health Checks

Ensure your container is healthy before the preview URL goes live:

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:3000/health || exit 1

Optimizing Docker Builds

Multi-stage Builds

Reduce image size with multi-stage builds:

# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/index.js"]

Layer Caching

Order your Dockerfile commands to maximize cache hits:

# Dependencies change less frequently
COPY package*.json ./
RUN npm ci

# Source code changes more frequently
COPY . .
RUN npm run build

Debugging Docker Previews

Access Container Logs

prev logs my-preview

Execute Commands in Container

prev exec my-preview -- sh

Check Resource Usage

prev stats my-preview

Best Practices

  • Keep images small: Use Alpine-based images when possible
  • Don't run as root: Create a non-root user in your Dockerfile
  • Use .dockerignore: Exclude unnecessary files from the build context
  • Pin versions: Use specific image tags, not latest
  • Scan for vulnerabilities: Integrate security scanning in your pipeline
  • Conclusion

    Docker and preview environments are a perfect match. With prev's Docker support, you can deploy complex applications with confidence, knowing that your preview environment mirrors production exactly.

    Start containerizing your previews today—your future self will thank you.