Skip to content

Docker Containerization

Connectum packages ship compiled JavaScript (.js + .d.ts + source maps), so they work on any Node.js version >= 18.0.0. If your own application code is written in TypeScript, you can either use Node.js 25+ (native type stripping for .ts files) or compile your code with a build tool before containerizing.

Full Example

All Docker files described below are available in the production-ready example.

Multi-Stage Dockerfile

Two-stage build: install dependencies in an isolated stage, then copy only production node_modules into a slim runtime image with a non-root user and health check.

See Dockerfile for the full listing.

Key highlights:

  • Stage 1 (deps) -- pnpm install --frozen-lockfile --prod for reproducible, minimal dependencies
  • Stage 2 (runtime) -- non-root connectum user, curl for HEALTHCHECK, native TypeScript via node src/index.ts
  • Environment defaults: NODE_ENV=production, PORT=5000, LOG_FORMAT=json, health and graceful shutdown enabled

Base image selection

If your own application code is compiled to JavaScript (e.g., via tsup or tsx), you can use any Node.js 18+ base image instead of node:25-slim. Use node:25-slim only when you want to run your own .ts files natively via Node.js type stripping.

Alternative Runtimes: Bun and tsx

You can replace the Node.js CMD with Bun or tsx:

dockerfile
# Node.js 25+ (native TypeScript for your own .ts files)
CMD ["node", "src/index.ts"]

# Bun
CMD ["bun", "src/index.ts"]

# tsx (works on Node.js 18+)
CMD ["npx", "tsx", "src/index.ts"]

When using tsx, you can use any Node.js 18+ base image (e.g., node:18-slim, node:20-slim, node:22-slim) and add tsx as a dependency. Since @connectum/* packages ship compiled JavaScript, no special loader is needed for any runtime.

Alpine Variant (Smaller Image)

If you need a smaller image and do not depend on native modules requiring glibc, use the Alpine variant.

See Dockerfile.alpine for the full listing.

Image Size Comparison

Base ImageApproximate SizeUse Case
node:25-slim~200 MBGeneral production (recommended)
node:25-alpine~140 MBSize-optimized, no native glibc modules
node:25~1 GBDevelopment only, avoid in production

.dockerignore

Keep images clean by excluding dependencies, tests, IDE files, dev configs, and proto sources.

See .dockerignore for the full listing.

Docker Compose for Local Development

A multi-service development environment with Connectum services, OpenTelemetry Collector, Jaeger, Prometheus, and Grafana.

See docker-compose.yml for the full listing.

Services included:

ServicePortDescription
order-service5000Connectum gRPC service
inventory-service5001Connectum gRPC service
otel-collector4317, 4318, 8889OpenTelemetry Collector (OTLP gRPC/HTTP, Prometheus)
jaeger16686Distributed tracing UI
prometheus9090Metrics collection
grafana3000Dashboards and visualization

OTel Collector Configuration

See otel-collector-config.yaml for the full listing.

The collector receives OTLP traces and metrics, batches them, and exports traces to Jaeger and metrics to Prometheus.

Image Optimization Tips

1. Layer Caching

Always copy package.json and pnpm-lock.yaml before source code. Docker caches the pnpm install layer and only re-runs it when dependencies change.

2. Production Dependencies Only

Use pnpm install --frozen-lockfile --prod to exclude devDependencies. This can reduce node_modules size by 50-70%.

3. Prune Unnecessary Files

After install, remove package manager caches:

dockerfile
RUN pnpm install --frozen-lockfile --prod \
    && pnpm store prune

4. Use .dockerignore Aggressively

Every file not needed at runtime should be in .dockerignore. This speeds up the build context transfer and reduces image size.

5. Pin Base Image Digests in Production

For reproducible builds, pin to a specific image digest:

dockerfile
FROM node:25-slim@sha256:<digest> AS runtime

WARNING

Never use the latest tag in production Dockerfiles. Always pin to a specific Node.js version (e.g., node:25.2.0-slim) to avoid unexpected breaking changes.

Environment Variables Reference

These environment variables configure a Connectum service inside a container:

VariableDescriptionDefault
NODE_ENVRuntime environmentdevelopment
PORTServer listen port5000
LISTENBind address0.0.0.0
LOG_LEVELLog verbosity (debug, info, warn, error)info
LOG_FORMATLog output format (json, pretty)json
LOG_BACKENDLogger backend (otel, pino, console)otel
HTTP_HEALTH_ENABLEDEnable HTTP health endpointsfalse
GRACEFUL_SHUTDOWN_ENABLEDEnable graceful shutdown on SIGTERM/SIGINTtrue
GRACEFUL_SHUTDOWN_TIMEOUT_MSShutdown timeout in ms30000
OTEL_SERVICE_NAMEOpenTelemetry service nameconnectum-service
OTEL_EXPORTER_OTLP_ENDPOINTOTLP collector endpoint--
OTEL_TRACES_EXPORTERTrace exporter (console, otlp/http, otlp/grpc, none)--
OTEL_METRICS_EXPORTERMetrics exporter--
OTEL_LOGS_EXPORTERLogs exporter--

What's Next