Skip to content

feat: add Docker support with multi-stage build#132

Merged
adnaan merged 2 commits intomainfrom
feature/docker-image
Feb 7, 2026
Merged

feat: add Docker support with multi-stage build#132
adnaan merged 2 commits intomainfrom
feature/docker-image

Conversation

@adnaan
Copy link
Contributor

@adnaan adnaan commented Feb 7, 2026

Summary

  • Add multi-stage Dockerfile for containerized deployment
  • Add .dockerignore for efficient builds
  • Final image size: 34MB (under 50MB target)

Details

3-stage build:

  1. node:20-alpine - builds TypeScript client
  2. golang:1.24-alpine - compiles Go binary
  3. alpine:3.21 - minimal runtime (34MB)

Features:

  • Non-root user for security
  • GOTOOLCHAIN=auto for Go version compatibility
  • Volume mounts work with live reload
  • ca-certificates and tzdata included

Usage

# Build
docker build -t tinkerdown .

# Run with volume mount
docker run -v $(pwd):/app -p 8080:8080 tinkerdown

Test plan

  • Docker image builds successfully
  • Image size under 50MB (34.1MB)
  • Volume mounts work
  • Live reload works with mounted files
  • Server accessible from host via port mapping
  • Publish to registry (follow-up task)

Closes #63

🤖 Generated with Claude Code

Add Dockerfile and .dockerignore for containerized deployment.

- Multi-stage build: Node.js (client) → Go (binary) → Alpine (runtime)
- Final image size: 34MB (under 50MB target)
- Non-root user for security
- Volume mounts work with live reload
- GOTOOLCHAIN=auto for future Go version compatibility

Usage:
  docker build -t tinkerdown .
  docker run -v $(pwd):/app -p 8080:8080 tinkerdown

Closes #63

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 7, 2026 08:11
@claude
Copy link

claude bot commented Feb 7, 2026

PR Review: Docker Support with Multi-Stage Build

Summary

This PR adds Docker support with a well-structured multi-stage build that successfully achieves the 34MB image size (well under the 50MB target). The implementation is solid overall with good security practices.

✅ Strengths

  1. Excellent multi-stage build structure - Properly separates build stages for client (Node) and server (Go), resulting in a minimal final image
  2. Security best practices - Non-root user, minimal Alpine base, includes ca-certificates
  3. Good .dockerignore - Comprehensive exclusions that will speed up builds
  4. Proper asset handling - Client assets are correctly built and copied into the Go binary location
  5. Size optimization - 34MB is excellent for a Go binary with embedded assets

🔍 Issues & Recommendations

Critical Issues

1. Go version mismatch (Dockerfile:12)

FROM golang:1.24-alpine AS go-builder

Issue: The Dockerfile specifies Go 1.24, but go.mod:3 requires Go 1.25.3. This will cause build failures.

Fix: Change to FROM golang:1.25-alpine AS go-builder

2. Missing CSS file copy (Dockerfile:32)
The glob pattern client/dist/tinkerdown-client.browser.* will only copy .js and .js.map files, missing the .css and .css.map files. Looking at the existing assets, both JS and CSS files are needed.

Fix: Explicitly copy all required files:

RUN mkdir -p internal/assets/client && \
    cp client/dist/tinkerdown-client.browser.js internal/assets/client/ && \
    cp client/dist/tinkerdown-client.browser.js.map internal/assets/client/ && \
    cp client/dist/tinkerdown-client.browser.css internal/assets/client/ && \
    cp client/dist/tinkerdown-client.browser.css.map internal/assets/client/

Medium Priority

3. Build arguments usage
The VERSION and BUILD_TIME build args are defined but not documented. Consider adding usage examples.

4. Missing CI/CD integration
No automated Docker build test in .github/workflows/ci.yml. Recommend adding a test job to verify builds and image size.

5. Missing healthcheck
For production deployments, consider adding a healthcheck (adds ~1MB for wget).

Minor Improvements

6. .dockerignore additions
Consider adding: *.log, .env*, *.sqlite-*

🧪 Test Coverage

Missing:

  • No automated Docker build test in CI/CD
  • No integration test for the Docker image

Recommendation: Add a CI job to test Docker builds and verify image size stays under 50MB.

🔒 Security

Good:

  • ✅ Non-root user (UID 1000)
  • ✅ Minimal Alpine base
  • ✅ ca-certificates included
  • ✅ No secrets in build args

Consider:

  • Using specific Alpine version tag for reproducibility
  • Scanning with docker scan or Trivy before publishing

⚡ Performance

Good:

  • ✅ Proper layer caching (dependencies before source)
  • CGO_ENABLED=0 for static binary
  • ✅ Build optimizations -ldflags="-s -w"

📋 Checklist Before Merge

  • Fix Go version to 1.25
  • Fix CSS file copying issue
  • Add CI job to test Docker builds
  • Document build args usage
  • Consider adding healthcheck (optional)
  • Test the actual Docker build locally with these fixes

Overall Assessment

This is a well-structured PR that demonstrates good Docker practices. The two critical issues (Go version and CSS files) must be fixed before merge, as they will cause build failures and missing styles. Once fixed, this will be a solid Docker implementation.

The 34MB image size is impressive and the security practices are sound. Great work! 🚀


Review generated by Claude Code

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds containerization support for the tinkerdown CLI via a multi-stage Docker build, aiming for a small runtime image and enabling volume-mounted development workflows.

Changes:

  • Introduces a 3-stage Dockerfile (Node client build → Go build → minimal Alpine runtime).
  • Adds a .dockerignore to reduce Docker build context and improve caching/build performance.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
Dockerfile Multi-stage build that compiles the TS client and Go binary, then ships a minimal non-root runtime image.
.dockerignore Excludes binaries, VCS metadata, docs, and build artifacts to speed up builds and reduce context size.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

ARG VERSION=dev
ARG BUILD_TIME=unknown
RUN CGO_ENABLED=0 GOOS=linux go build \
-ldflags="-s -w -X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}" \
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ldflags here attempt to set main.version and main.buildTime via -X, but the cmd/tinkerdown main package currently defines version as a const and does not define buildTime at all. go build will fail with -X main.version=...: not a var / missing symbol. Consider switching these to var values in the main package (with defaults) or updating the ldflags to match existing variables.

Suggested change
-ldflags="-s -w -X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}" \
-ldflags="-s -w" \

Copilot uses AI. Check for mistakes.
Dockerfile Outdated
Comment on lines 12 to 21
FROM golang:1.24-alpine AS go-builder

# Install git for version info and ca-certificates for HTTPS
RUN apk add --no-cache git ca-certificates

WORKDIR /app

# Allow Go to download required toolchain version automatically
ENV GOTOOLCHAIN=auto

Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go.mod specifies go 1.25.3, but this builder stage pins golang:1.24-alpine. Even with GOTOOLCHAIN=auto, this can force an extra toolchain download (hurting build reliability in restricted networks) or fail if the toolchain can't be fetched. Consider aligning the builder image tag to the Go version required by go.mod (or document why you intentionally rely on toolchain auto-download).

Copilot uses AI. Check for mistakes.
- Update Go base image to 1.25-alpine (matches go.mod requirement)
- Make CSS/JS file copying explicit for clarity
- Add additional patterns to .dockerignore (*.log, .env*, *.sqlite-*)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Feb 7, 2026

PR Review: Docker Support with Multi-Stage Build

Overall, this is a well-crafted PR that adds Docker support with an efficient multi-stage build. The implementation is clean and follows best practices.

✅ Strengths

  1. Excellent Multi-Stage Build Design - Clean separation across 3 stages, 34MB final image
  2. Security Best Practices - Non-root user (UID 1000), CGO_ENABLED=0, ca-certificates included
  3. Build Optimizations - Good caching, GOTOOLCHAIN=auto, stripped binary with ldflags
  4. Comprehensive .dockerignore - Well-organized, excludes dev artifacts

🔍 Critical Issue

Go Version Mismatch (Dockerfile:12)

  • Uses golang:1.25-alpine but this image doesn't exist yet (latest is 1.24)
  • go.mod specifies go 1.25.3
  • Fix: Change to golang:1.24-alpine until 1.25 is released
  • The GOTOOLCHAIN=auto will download 1.25.3 toolchain automatically

📋 High Priority Recommendations

  1. Inconsistent Asset Handling - Dockerfile explicitly copies 4 files while Makefile uses wildcard. Consider using same pattern.
  2. Document Build Args - Show users how to pass VERSION and BUILD_TIME during build
  3. Port Mapping Docs - Clarify that users need to map ports matching their config

📋 Medium/Low Priority

  • Consider adding HEALTHCHECK instruction (needs wget/curl in final stage)
  • Document multi-platform builds with buildx
  • Consider pinning base images to SHA256 for reproducibility

🧪 Testing Recommendations

  1. Verify build works
  2. Test with version args passed to build command
  3. Verify non-root user runs correctly
  4. Test volume mounts with live reload

✅ Final Verdict

High-quality PR - Nearly ready to merge after fixing the critical Go version issue.

Recommendation: ✅ Approve after changing to golang:1.24-alpine

Other suggestions can be follow-up PRs. Great work on the multi-stage build and achieving 34MB image size!


Review by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5.5 Docker Image

1 participant