Skip to content

feat: Add multi-stage Docker build for production deployment - #59

Closed
VAIBHAVSING wants to merge 6 commits into
mainfrom
feature/multi-stage-docker-build
Closed

feat: Add multi-stage Docker build for production deployment#59
VAIBHAVSING wants to merge 6 commits into
mainfrom
feature/multi-stage-docker-build

Conversation

@VAIBHAVSING

@VAIBHAVSING VAIBHAVSING commented Oct 26, 2025

Copy link
Copy Markdown
Owner

Summary

This PR introduces a multi-stage Docker build system for production deployment to Docker Hub.

Changes

1. New Multi-Stage Dockerfile (docker/Dockerfile.production)

  • Combines all 4 Docker layers into a single multi-stage build
  • Stage 1: Workspace Supervisor (Go binary builder)
  • Stage 2: Base System (Ubuntu + SSH + tools)
  • Stage 3: Language Runtimes (Node.js, Python, Go, Rust, Bun)
  • Stage 4: VS Code Server
  • Stage 5: AI Tools (GitHub CLI, Azure CLI, yq) - Final Image

2. Updated GitHub Actions Workflow (.github/workflows/docker-cd-production.yml)

  • Simplified workflow that builds the entire stack in one multi-stage build
  • Only uploads the final workspace image to Docker Hub: vaibhavsing/dev8-workspace
  • Added comprehensive testing for all components (languages, tools, services)
  • Added security scanning with Trivy
  • Triggers on push to main or production branches, and PRs
  • Manual workflow dispatch option

Local Development

# Build all layers locally
cd docker && make build-all

# Run with docker-compose
docker compose up -d

# Access VS Code Server
open http://localhost:8080

Docker Hub Deployment

When merged to main or production branch, the workflow will:

  1. Build the multi-stage Docker image
  2. Run comprehensive tests
  3. Scan for vulnerabilities with Trivy
  4. Push only the final image to Docker Hub as vaibhavsing/dev8-workspace:latest

Testing Checklist

  • Multi-stage build completes successfully
  • All language runtimes are installed and functional
  • VS Code Server is accessible
  • AI tools (gh, az, yq) are installed
  • Security scan passes (or acceptable vulnerabilities documented)
  • Image is pushed to Docker Hub
  • Workflow succeeds on GitHub Actions

Images Produced

  • vaibhavsing/dev8-workspace:latest
  • vaibhavsing/dev8-workspace:<version>
  • vaibhavsing/dev8-workspace:<branch>-<sha>

Summary by CodeRabbit

  • Chores
    • Streamlined Docker deployment workflow with multi-stage layered builds for improved efficiency.
    • Enhanced pull request feedback with automated build summaries posted as comments.
    • Consolidated testing and security scanning on final container images.
    • Pull request builds now tagged with pr-<number> for easier identification and management.

- Add push_to_dockerhub input for workflow_dispatch
- Set force_rebuild default to true for manual triggers
- Use fallback DockerHub username (vaibhavsing) if secret not set
- Add conditional push logic based on input
- Fix all image build/push steps to use dynamic username
- Remove unused dockerhub_username output from setup job
- Create Dockerfile.production with all 4 layers as multi-stage build
- Update docker-cd-production.yml workflow to build and push only final image
- Workflow builds: supervisor, base, languages, vscode, and ai-tools layers
- Only final workspace image (vaibhavsing/dev8-workspace) pushed to Docker Hub
- Added comprehensive testing for all components
- Added security scanning with Trivy
- Local development: cd docker && make build-all && docker compose up -d
@coderabbitai

coderabbitai Bot commented Oct 26, 2025

Copy link
Copy Markdown
Contributor

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description does not follow the provided template structure. While the description contains comprehensive and well-organized information about the changes, local development, and deployment, it is missing several required template sections: the Type of Change checkbox selection, a Related Issue reference (if applicable), the standard Testing checklist with checkboxes, the standard PR Checklist section, and the Environment Tested section. The description uses a custom format instead of the repository's established template, which diverges from the expected structure for consistency. Please revise the description to follow the provided template structure. Add a Type of Change section with the appropriate checkbox selected (likely ✨ New feature or ♻️ Code refactoring), include a Related Issue reference if this addresses an open issue, use the standard Testing section with checkboxes rather than a custom testing checklist, complete the standard PR Checklist section, and add an Environment Tested section if applicable. The custom information currently provided can be incorporated into the relevant template sections.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat: Add multi-stage Docker build for production deployment" accurately captures the primary change in the pull request. The changeset consolidates Docker layers into a multi-stage build approach and updates the CI/CD workflow, and the title clearly conveys this main objective. It's concise, uses conventional commit formatting, and is specific enough that a teammate scanning commit history would immediately understand the core change. The title is neither vague nor off-topic.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/multi-stage-docker-build

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

The sha tag with branch prefix was generating invalid tags like
'vaibhavsing/dev8-workspace:-7f4a660' which caused build to fail.
Removed type=sha,prefix={{branch}}- from metadata tags.
Changes:
- Removed Dockerfile.production (multi-stage approach)
- Updated workflow to use 'make build-all' for layered builds
- Builds: base → languages → vscode → ai-tools (separately)
- Only pushes final dev8-workspace container to Docker Hub
- Container is ready for ACI deployment with volume support
- Local dev: cd docker && make build-all && docker compose up -d
Docker compose validates environment variables even during build.
Set a dummy token to allow builds to proceed in CI.
Issue: docker compose build tries to pull from Docker Hub after building
base layer, but images are only available locally.

Solution: Use direct 'docker build' commands for each layer to ensure
they use locally built images as base.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
.github/workflows/docker-cd-production.yml (4)

1-6: Semantic clarification: Separate layer builds vs. true multi-stage Docker build

The PR title and description indicate a "multi-stage Docker build," but the workflow builds four separate Docker images sequentially (lines 77–98), not a true multi-stage build using FROM statements within a single Dockerfile. The distinction matters:

  • Current approach: Builds dev8-base, dev8-languages, dev8-vscode, and dev8-workspace as independent images—useful for layer caching and independent testing, but increases storage and build complexity.
  • True multi-stage: Uses a single Dockerfile with multiple FROM statements, producing only the final image by default.

Clarify whether the separate-image approach is intentional for your use case (e.g., reusing base layers in other workflows) or if a single Dockerfile with true multi-stage build would be preferable. This affects documentation and expectations.

If you'd like to refactor to a true multi-stage Dockerfile, I can help generate that. Otherwise, consider updating the PR title and description to reflect "separate layered Docker builds" for accuracy.


77-98: Add set -e or explicit error handling for layer builds

Each build step (lines 83, 87, 91, 95) lacks explicit failure detection. If any layer build fails, the workflow continues to subsequent steps (tagging, testing, pushing) with a stale or missing image, causing confusing failures downstream.

Add set -e at the top of the script to exit on any error:

      - name: Build all Docker layers
        run: |
+         set -e
          echo "🏗️ Building all Docker layers (base → languages → vscode → ai-tools)..."
          
          # Build Layer 1: Base
          echo "Building base layer..."
          docker build -t dev8-base:latest -f ./docker/images/00-base/Dockerfile .

Alternatively, chain with explicit error messages:

          docker build -t dev8-base:latest -f ./docker/images/00-base/Dockerfile . || { echo "❌ Base layer build failed"; exit 1; }

112-142: Test container: simplify multi-line command or use dedicated test script

The single-line docker run command with multiple chained && operators (lines 115–141) is difficult to debug if a component fails. The error output will not clearly identify which test step failed.

Consider moving the test commands to a dedicated shell script in the repository (e.g., docker/test-workspace.sh), then invoke it:

      - name: Test final workspace container
        run: |
-         echo "=== Testing Final Workspace Container ==="
-         docker run --rm dev8-workspace:latest bash -c "
-           echo '--- Testing Workspace Supervisor ---' &&
-           workspace-supervisor --version &&
-           ...
-         "
+         echo "=== Testing Final Workspace Container ==="
+         docker run --rm dev8-workspace:latest bash ./docker/test-workspace.sh

This improves:

  • Readability and maintainability
  • Error attribution (clearer which test fails)
  • Reusability (same script for local and CI testing)
  • Easier iteration during development

99-111: Minor: consolidate conditional tagging logic

The conditional tagging logic is correct but somewhat verbose. Consider consolidating:

      - name: Tag final workspace image for Docker Hub
        run: |
          echo "🏷️ Tagging final workspace image..."
-         docker tag dev8-workspace:latest ${{ env.DOCKERHUB_IMAGE }}:${{ steps.version.outputs.version }}
-         docker tag dev8-workspace:latest ${{ env.DOCKERHUB_IMAGE }}:latest
-         if [ "${{ github.ref }}" = "refs/heads/production" ]; then
-           docker tag dev8-workspace:latest ${{ env.DOCKERHUB_IMAGE }}:production
-         fi
-         if [ "${{ github.event_name }}" = "pull_request" ]; then
-           docker tag dev8-workspace:latest ${{ env.DOCKERHUB_IMAGE }}:pr-${{ github.event.pull_request.number }}
-         fi
+         # Always tag with version and latest
+         docker tag dev8-workspace:latest ${{ env.DOCKERHUB_IMAGE }}:${{ steps.version.outputs.version }}
+         docker tag dev8-workspace:latest ${{ env.DOCKERHUB_IMAGE }}:latest
+         
+         # Production branch gets a production tag
+         [ "${{ github.ref }}" = "refs/heads/production" ] && \
+           docker tag dev8-workspace:latest ${{ env.DOCKERHUB_IMAGE }}:production
+         
+         # PRs get a pr-<number> tag
+         [ "${{ github.event_name }}" = "pull_request" ] && \
+           docker tag dev8-workspace:latest ${{ env.DOCKERHUB_IMAGE }}:pr-${{ github.event.pull_request.number }}
+         
          echo "✅ Images tagged"

This reduces repetition and improves clarity of the tagging strategy.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4add342 and a123550.

📒 Files selected for processing (1)
  • .github/workflows/docker-cd-production.yml (1 hunks)
🔇 Additional comments (3)
.github/workflows/docker-cd-production.yml (3)

160-172: Potential issue: numfmt command availability

Line 167 uses numfmt --to=iec-i --suffix=B to format the image size. While numfmt is part of GNU coreutils, it may not be available or in the PATH on all ubuntu-latest runners.

Verify that numfmt is available on ubuntu-latest. If unavailable, consider a portable alternative:

# Using awk and printf (portable)
SIZE=$(docker inspect dev8-workspace:latest --format='{{.Size}}')
# Convert bytes to human-readable (rough implementation)
echo $SIZE | awk '{
  for (i=1; i<=log($1)/log(1024); i++) {}
  printf "%.2f %s\n", $1/1024^i, ["B", "KiB", "MiB", "GiB", "TiB"][i+1]
}'

Or use python for more robust formatting (available on ubuntu-latest):

python3 -c "import os; s=$(docker inspect dev8-workspace:latest --format='{{.Size}}'); print(f'{s / (1024**3):.2f} GiB' if s > 1024**3 else f'{s / (1024**2):.2f} MiB')"

173-190: Verify push condition logic for workflow_dispatch

Line 177 conditions the Docker Hub push on:

  • NOT a pull_request event
  • AND (main branch OR production branch OR workflow_dispatch with push_to_dockerhub=true)

This is correct, but ensure the workflow_dispatch input parsing is reliable. The YAML input 'true' is a string, and the comparison on line 177 is a string comparison—confirm this works as expected in practice.

Consider adding explicit logging for the push decision:

      - name: Push final workspace container to Docker Hub
        if: |
          github.event_name != 'pull_request' && 
          (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/production' ||
           (github.event_name == 'workflow_dispatch' && github.event.inputs.push_to_dockerhub == 'true'))
        run: |
+         echo "Event: ${{ github.event_name }}"
+         echo "Ref: ${{ github.ref }}"
+         echo "Push to Docker Hub: yes"
          echo "🚀 Pushing final workspace container to Docker Hub..."
          docker push ${{ env.DOCKERHUB_IMAGE }}:${{ steps.version.outputs.version }}
          docker push ${{ env.DOCKERHUB_IMAGE }}:latest

This helps with debugging if pushes are unexpectedly skipped.


272-327: Approve PR comment generation

The Comment PR with container info step correctly:

  • Uses github-script@v7 to post a comment
  • Provides clear instructions for local development and production deployment
  • Only runs on pull_request events
  • Includes build approach and test status

No issues identified. This improves reviewer experience by providing context directly in the PR.

Comment on lines +191 to 270
- name: Deployment summary
if: github.event_name != 'pull_request'
run: |
{
echo "# 🚀 Docker Production Deployment Summary"
echo ""
echo "## 📦 Deployment Information"
echo "- **Version**: \`${{ needs.setup.outputs.version }}\`"
echo "- **Version**: \`${{ steps.version.outputs.version }}\`"
echo "- **Commit**: \`${{ github.sha }}\`"
echo "- **Branch**: \`${{ github.ref_name }}\`"
echo "- **Timestamp**: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
echo ""
echo "## 🏗️ Build Status"
echo "## 🏗️ Build Approach"
echo ""
echo "Built using **layered Docker images**:"
echo "1. � dev8-base (Ubuntu + SSH + Supervisor)"
echo "2. 🔹 dev8-languages (Node.js, Python, Go, Rust, Bun)"
echo "3. 🔹 dev8-vscode (VS Code Server)"
echo "4. 🔹 **dev8-workspace** (AI Tools - Final Container)"
echo ""
echo "| Image | Status | Pushed to Docker Hub |"
echo "|-------|--------|----------------------|"
echo "| dev8-base | ${{ needs.build-base.result }} | ${{ needs.build-base.result == 'success' && '✅' || '❌' }} |"
echo "| dev8-languages | ${{ needs.build-languages.result }} | ${{ needs.build-languages.result == 'success' && '✅' || '❌' }} |"
echo "| dev8-vscode | ${{ needs.build-vscode.result }} | ${{ needs.build-vscode.result == 'success' && '✅' || '❌' }} |"
echo "| dev8-workspace | ${{ needs.build-ai-tools.result }} | ${{ needs.build-ai-tools.result == 'success' && '✅' || '❌' }} |"
echo "## �🐳 Docker Hub - Final Container Only"
echo ""
echo "## 🔗 Docker Hub Images"
echo "**Image**: \`${{ env.DOCKERHUB_IMAGE }}\`"
echo ""
echo "Pull commands:"
echo "### Pull Commands:"
echo "\`\`\`bash"
echo "# Base image"
echo "docker pull ${{ needs.setup.outputs.dockerhub_username }}/dev8-base:${{ needs.setup.outputs.version }}"
echo "# Latest version"
echo "docker pull ${{ env.DOCKERHUB_IMAGE }}:latest"
echo ""
echo "# Languages image"
echo "docker pull ${{ needs.setup.outputs.dockerhub_username }}/dev8-languages:${{ needs.setup.outputs.version }}"
echo "# Specific version"
echo "docker pull ${{ env.DOCKERHUB_IMAGE }}:${{ steps.version.outputs.version }}"
echo "\`\`\`"
echo ""
echo "# VS Code image"
echo "docker pull ${{ needs.setup.outputs.dockerhub_username }}/dev8-vscode:${{ needs.setup.outputs.version }}"
echo "## 🏃 Run Container (Production)"
echo ""
echo "# Workspace image (recommended)"
echo "docker pull ${{ needs.setup.outputs.dockerhub_username }}/dev8-workspace:${{ needs.setup.outputs.version }}"
echo "docker pull ${{ needs.setup.outputs.dockerhub_username }}/dev8-workspace:latest"
echo "docker pull ${{ needs.setup.outputs.dockerhub_username }}/dev8-workspace:production"
echo "\`\`\`bash"
echo "# Run workspace container with volume support"
echo "docker run -d \\"
echo " --name dev8-workspace \\"
echo " -p 8080:8080 \\"
echo " -p 2222:2222 \\"
echo " -p 9000:9000 \\"
echo " -e GITHUB_TOKEN=\${GITHUB_TOKEN} \\"
echo " -e ENVIRONMENT_ID=dev8-prod-001 \\"
echo " -v dev8-home:/home/dev8 \\"
echo " -v dev8-workspace:/workspace \\"
echo " ${{ env.DOCKERHUB_IMAGE }}:latest"
echo "\`\`\`"
echo ""
echo "## 🔐 Security Scans"
echo "## 🧪 Test Results"
echo ""
echo "Trivy vulnerability scans completed. Check GitHub Security tab for detailed results."
echo "✅ All component tests passed:"
echo "- Workspace Supervisor"
echo "- Node.js (with npm, pnpm)"
echo "- Python (with pip, poetry)"
echo "- Go"
echo "- Rust"
echo "- Bun"
echo "- VS Code Server"
echo "- GitHub CLI"
echo "- Azure CLI"
echo "- yq"
echo ""
echo "## 📝 Next Steps"
echo "## 🔐 Security Scan"
echo ""
echo "1. Review security scan results in GitHub Security"
echo "2. Update production deployments to use version \`${{ needs.setup.outputs.version }}\`"
echo "3. Test in staging environment before full rollout"
echo "4. Monitor application logs after deployment"
echo "Trivy vulnerability scan completed. Check GitHub Security tab for detailed results."
echo ""
echo "---"
echo "## 📝 Local Development"
echo ""
echo "\`\`\`bash"
echo "# Build all layers locally"
echo "cd docker && make build-all"
echo ""
echo "# Run with docker-compose"
echo "docker compose up -d"
echo "\`\`\`"
echo ""
echo "---"
echo "**Deployment completed at**: $(date -u +'%Y-%m-%d %H:%M:%S UTC')"
} >> "$GITHUB_STEP_SUMMARY"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Character encoding issue in deployment summary output

Lines 206 and 211 contain visually corrupted emoji characters:

  • Line 206: "1. � dev8-base (Ubuntu + SSH + Supervisor)" – appears to be a mojibake or corrupted emoji
  • Line 211: "## 🐳 Docker Hub - Final Container Only" – the 🐳 may not render consistently

Replace with safe, ASCII-compatible alternatives or test emoji rendering:

-            echo "1. � dev8-base (Ubuntu + SSH + Supervisor)"
-            echo "2. 🔹 dev8-languages (Node.js, Python, Go, Rust, Bun)"
-            echo "3. 🔹 dev8-vscode (VS Code Server)"
-            echo "4. 🔹 **dev8-workspace** (AI Tools - Final Container)"
+            echo "1. 🐳 dev8-base (Ubuntu + SSH + Supervisor)"
+            echo "2. 🔹 dev8-languages (Node.js, Python, Go, Rust, Bun)"
+            echo "3. 🔹 dev8-vscode (VS Code Server)"
+            echo "4. 🔹 **dev8-workspace** (AI Tools - Final Container)"

And:

-            echo "## 🐳 Docker Hub - Final Container Only"
+            echo "## Docker Hub - Final Container Only 🐳"

@VAIBHAVSING
VAIBHAVSING deleted the feature/multi-stage-docker-build branch October 26, 2025 15:01
@VAIBHAVSING

Copy link
Copy Markdown
Owner Author

mirror #60

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.

1 participant