📦
Bunty

Deploy with Docker

Docker provides a consistent and portable way to package and deploy Bunty applications. This guide covers creating optimized Docker images and running containers in production.

Quick Start

Basic Dockerfile

Create a Dockerfile in your project root:

# Use Bun's official image
FROM oven/bun:1 as base
WORKDIR /app

# Install dependencies
FROM base AS install
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

# Build application (if needed)
FROM install AS build
COPY . .
# RUN bun run build

# Production image
FROM base AS production
COPY --from=install /app/node_modules ./node_modules
COPY --from=build /app .

# Expose port
EXPOSE 3000

# Set environment
ENV NODE_ENV=production

# Run the application
CMD ["bun", "run", "src/main.ts"]

Build and Run

# Build image
docker build -t bunty-app .

# Run container
docker run -p 3000:3000 bunty-app

# Run with environment variables
docker run -p 3000:3000 \
  -e DATABASE_URL=postgresql://localhost/mydb \
  -e REDIS_URL=redis://localhost:6379 \
  bunty-app

Multi-Stage Build (Optimized)

Production-Ready Dockerfile

# syntax=docker/dockerfile:1

# Base stage
FROM oven/bun:1-alpine as base
WORKDIR /app

# Dependencies stage
FROM base AS dependencies
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile --production

# Development dependencies stage
FROM base AS dev-dependencies
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

# Build stage
FROM dev-dependencies AS build
COPY . .
# Add build steps if needed
# RUN bun run build
RUN bun test

# Production stage
FROM base AS production

# Create non-root user
RUN addgroup -g 1001 -S bunty && \
    adduser -S bunty -u 1001

# Copy dependencies and application
COPY --from=dependencies --chown=bunty:bunty /app/node_modules ./node_modules
COPY --from=build --chown=bunty:bunty /app .

# Switch to non-root user
USER bunty

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD bun run healthcheck.ts || exit 1

# Expose port
EXPOSE 3000

# Set environment
ENV NODE_ENV=production \
    PORT=3000

# Run application
CMD ["bun", "run", "src/main.ts"]

Health Check Script

Create healthcheck.ts:

const response = await fetch('http://localhost:3000/health');
if (!response.ok) {
  process.exit(1);
}
process.exit(0);

Docker Compose

Single Service

Create docker-compose.yml:

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - PORT=3000
      - DATABASE_URL=postgresql://postgres:password@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    networks:
      - bunty-network

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=mydb
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - bunty-network

  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data
    networks:
      - bunty-network

volumes:
  postgres-data:
  redis-data:

networks:
  bunty-network:
    driver: bridge

Full Stack with Load Balancer

version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - app
    networks:
      - bunty-network

  app:
    build:
      context: .
      dockerfile: Dockerfile
    deploy:
      replicas: 3
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    networks:
      - bunty-network

  worker:
    build:
      context: .
      dockerfile: Dockerfile
    command: ["bun", "run", "src/worker.ts"]
    deploy:
      replicas: 2
    environment:
      - NODE_ENV=production
      - REDIS_URL=redis://redis:6379
    depends_on:
      - redis
    networks:
      - bunty-network

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=mydb
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - bunty-network

  redis:
    image: redis:7-alpine
    volumes:
      - redis-data:/data
    networks:
      - bunty-network

volumes:
  postgres-data:
  redis-data:

networks:
  bunty-network:
    driver: bridge

Run with Docker Compose

# Start all services
docker-compose up -d

# View logs
docker-compose logs -f

# Scale services
docker-compose up -d --scale app=5

# Stop services
docker-compose down

# Remove volumes
docker-compose down -v

Environment Variables

.env File

Create .env:

NODE_ENV=production
PORT=3000
DATABASE_URL=postgresql://postgres:password@db:5432/mydb
REDIS_URL=redis://redis:6379
JWT_SECRET=your-secret-key
LOG_LEVEL=info

Use in Docker Compose

services:
  app:
    build: .
    env_file:
      - .env
    # or
    environment:
      - NODE_ENV=${NODE_ENV}
      - PORT=${PORT}

Docker Secrets (Swarm)

version: '3.8'

services:
  app:
    image: bunty-app:latest
    secrets:
      - db_password
      - jwt_secret
    environment:
      - DATABASE_PASSWORD_FILE=/run/secrets/db_password
      - JWT_SECRET_FILE=/run/secrets/jwt_secret

secrets:
  db_password:
    external: true
  jwt_secret:
    external: true

Optimization Tips

1. Use .dockerignore

Create .dockerignore:

node_modules
.git
.github
.vscode
.env
.env.*
*.log
dist
build
coverage
.DS_Store
README.md
docker-compose.yml
Dockerfile
.dockerignore

2. Multi-Architecture Builds

# Build for multiple platforms
docker buildx build --platform linux/amd64,linux/arm64 -t bunty-app:latest .

3. Layer Caching

# Copy package files first (changes less frequently)
COPY package.json bun.lockb ./
RUN bun install

# Copy source code (changes more frequently)
COPY . .

4. Minimize Image Size

# Use alpine base
FROM oven/bun:1-alpine

# Remove unnecessary files
RUN rm -rf /var/cache/apk/* /tmp/*

# Use multi-stage builds
# Copy only production dependencies

Production Best Practices

1. Security

# Don't run as root
USER bunty

# Scan for vulnerabilities
# docker scan bunty-app

2. Resource Limits

services:
  app:
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 1G
        reservations:
          cpus: '1'
          memory: 512M

3. Health Checks

services:
  app:
    healthcheck:
      test: ["CMD", "bun", "run", "healthcheck.ts"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 40s

4. Logging

services:
  app:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

5. Restart Policy

services:
  app:
    restart: unless-stopped
    # or
    deploy:
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3

Registry and Deployment

Push to Docker Hub

# Tag image
docker tag bunty-app:latest yourusername/bunty-app:latest
docker tag bunty-app:latest yourusername/bunty-app:v1.0.0

# Login
docker login

# Push
docker push yourusername/bunty-app:latest
docker push yourusername/bunty-app:v1.0.0

Private Registry

# Tag for private registry
docker tag bunty-app registry.example.com/bunty-app:latest

# Login
docker login registry.example.com

# Push
docker push registry.example.com/bunty-app:latest

Monitoring

Container Stats

# View stats
docker stats

# View logs
docker logs -f container_id

# Inspect container
docker inspect container_id

Integration with Monitoring Tools

services:
  app:
    labels:
      - "prometheus.io/scrape=true"
      - "prometheus.io/port=3000"
      - "prometheus.io/path=/metrics"

Next Steps

Have questions? Join our Discord community
Found an issue? Edit this page on GitHub