Skip to content

Deploy Wyn Apps with Docker

Wyn compiles to a single static binary. That means your Docker images can be tiny — 5MB or less using a scratch base image. No runtime, no interpreter, no dependency layer.

Why Wyn + Docker Works Well

MetricWynPythonNode.jsGo
Docker image size~5MB~150MB~180MB~15MB
Startup time<1ms~500ms~200ms~5ms
Runtime dependenciesNonePython + pip packagesNode + npm packagesNone
Files in image1 binaryHundredsThousands1 binary

Wyn produces a statically-linked binary with no external dependencies. The entire container is your binary plus a scratch base layer.

Basic Dockerfile

dockerfile
# Build stage
FROM ubuntu:22.04 AS build
RUN apt-get update && apt-get install -y curl gcc
RUN curl -fsSL https://wynlang.com/install.sh | sh
WORKDIR /app
COPY . .
RUN wyn build main.wyn --release --target linux-x86_64 -o server

# Runtime stage — scratch means empty base image
FROM scratch
COPY --from=build /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]

Build and run:

sh
docker build -t myapp .
docker run -p 8080:8080 myapp

The final image contains exactly one file: your compiled binary.

Multi-Stage Build with SQLite

If your app uses SQLite, you need the SQLite shared library. Use alpine instead of scratch:

dockerfile
FROM ubuntu:22.04 AS build
RUN apt-get update && apt-get install -y curl gcc libsqlite3-dev
RUN curl -fsSL https://wynlang.com/install.sh | sh
WORKDIR /app
COPY . .
RUN wyn build main.wyn --release --target linux-x86_64 -o server

FROM alpine:3.19
RUN apk add --no-cache sqlite-libs
COPY --from=build /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]

This adds ~8MB for Alpine + SQLite, bringing the total to ~13MB.

Docker Compose Example

yaml
version: "3.8"
services:
  api:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DB_PATH=/data/app.db
    volumes:
      - app-data:/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3

volumes:
  app-data:

Production Checklist

  1. Use --release — produces optimized binaries (49KB hello world vs 425KB debug)
  2. Use scratch or alpine — don't ship Ubuntu in production
  3. Set a non-root user if using Alpine:
    dockerfile
    RUN adduser -D appuser
    USER appuser
  4. Add a health check endpoint in your Wyn app:
    wyn
    if path == "/health" {
        Http.respond(req, 200, "ok")
    }
  5. Pin your base image versionsalpine:3.19, not alpine:latest

Image Size Comparison

Real-world web API (HTTP server + JSON + routing):

StackImage Size
Wyn (scratch)4.8MB
Wyn (alpine + SQLite)13MB
Go (scratch)12MB
Rust (scratch)8MB
Node.js (alpine)180MB
Python (slim)150MB

Cross-Compilation for Docker

Build a Linux binary from macOS without Docker:

sh
wyn build main.wyn --release --target linux-x86_64 -o server

Then copy the binary into a minimal Dockerfile:

dockerfile
FROM scratch
COPY server /server
EXPOSE 8080
ENTRYPOINT ["/server"]

This skips the build stage entirely — useful for CI pipelines where you build the binary separately.

Kubernetes Deployment

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wyn-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: wyn-api
  template:
    metadata:
      labels:
        app: wyn-api
    spec:
      containers:
        - name: api
          image: registry.example.com/wyn-api:latest
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "16Mi"
              cpu: "50m"
            limits:
              memory: "64Mi"
              cpu: "200m"
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 1
            periodSeconds: 10

Wyn's low memory footprint (1-2MB RSS for a hello world server) means you can run many replicas on minimal resources.

See Also

MIT License — v1.11