learning_ai_invt_trdg/docs/DOCKER_DEPLOYMENT_GUIDE.md
root 5d9755fc0a docs: add Docker deployment guide and update smoke script
- Add comprehensive Docker deployment guide
- Update smoke-release.sh for compatibility

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-10 00:27:17 +00:00

1061 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Docker Deployment Guide - Shared Base Image Strategy
> **Purpose:** Comprehensive guide for Docker deployment across ByteLyst products using shared base images to minimize disk duplication and optimize build times.
>
> **Target Audience:** DevOps engineers, product teams deploying ByteLyst products.
>
> **Last Updated:** 2026-05-09
---
## Overview
This guide documents the shared base image strategy for Docker deployment across all ByteLyst products. This approach minimizes disk space duplication by building a single base image containing all `@bytelyst/*` shared packages, which all product images then inherit from.
### Problem Statement
**Without shared base images:**
- Each Docker image includes duplicate `@bytelyst/*` packages (~100-200MB per image)
- 3 products = ~300-600MB wasted disk space
- 20+ products = ~2-4GB wasted disk space
- Slower builds (reinstalling same packages in each image)
- Inconsistent package versions across products
**With shared base images:**
- Common packages stored once in base image (~200MB)
- Each product image only contains product-specific code (~50MB)
- Docker layer sharing means packages stored once on disk
- Faster builds (base image cached)
- Consistent package versions across all products
### Architecture
```
bytelyst-common-base:latest (Built once, ~200MB)
├── node:22-slim
├── pnpm@10.6.5
├── @bytelyst/* packages (63 packages, shared layer)
└── Build tools (typescript, etc.)
Product Images (inherit from base):
├── learning_ai_notes-backend:latest (~50MB additional)
│ └── Inherits from bytelyst-common-base
│ └── + backend code
├── learning_ai_clock-backend:latest (~50MB additional)
│ └── Inherits from bytelyst-common-base
│ └── + backend code
└── learning_ai_invt_trdg-backend:latest (~50MB additional)
└── Inherits from bytelyst-common-base
└── + backend code
```
**Disk usage comparison:**
- Without base: 3 products × ~250MB = ~750MB
- With base: ~200MB (base) + 3 × ~50MB = ~350MB
- **Savings: ~400MB (53% reduction)**
---
## Part 1: Base Image Strategy
### 1.1 Separate Base Images for Backend and Web
**Rationale:** Backend (Fastify) and web (Next.js) have different dependency sets. Separate base images optimize for each use case.
**Base Images:**
- `bytelyst-common-base-backend:latest` - Backend dependencies (Fastify, Zod, jose, etc.)
- `bytelyst-common-base-web:latest` - Web dependencies (Next.js, React, Tailwind, etc.)
### 1.2 Base Image Versioning Strategy
**Semantic Versioning:**
- Format: `bytelyst-common-base-backend:1.0.0`
- Increment major version for breaking changes
- Increment minor version for new packages
- Increment patch version for bug fixes
**Date-Based Tags (for automation):**
- Format: `bytelyst-common-base-backend:20260509`
- Useful for automated builds
- Easy to identify build date
**Always maintain `:latest` tag:**
- Points to most recent stable version
- Used by default in product Dockerfiles
- Update only after testing
### 1.3 Base Image Location
**Build in common plat repo:**
- Location: `learning_ai_common_plat/Dockerfile.backend-base`
- Build context: Common plat repo (has access to all packages)
- Push to registry: Docker Hub, Gitea container registry, or local registry
**Registry Options:**
1. **Docker Hub** - Public, but requires organization account
2. **Gitea Container Registry** - Private, integrated with existing Gitea
3. **Local Registry** - Fastest for single VM, requires registry setup
4. **GitHub Container Registry** - If using GitHub Actions
**Recommendation:** Use Gitea Container Registry for privacy and integration.
---
## Part 2: Base Image Implementation
### 2.1 Backend Base Image
**File:** `learning_ai_common_plat/Dockerfile.backend-base`
```dockerfile
# ── Stage 1: Builder ─────────────────────────────────────────────────────
FROM node:22-slim AS builder
# Install pnpm
RUN corepack enable && corepack prepare pnpm@10.6.5 --activate
WORKDIR /app
# Copy workspace files
COPY .npmrc .pnpmfile.cjs pnpm-workspace.yaml pnpm-lock.yaml* ./
# Copy all packages
COPY packages/ ./packages/
# Build all @bytelyst/* packages
RUN pnpm -r --filter './packages/*' build
# ── Stage 2: Production Base ───────────────────────────────────────────────
FROM node:22-slim
# Metadata
LABEL org.opencontainers.image.title="ByteLyst Common Base - Backend"
LABEL org.opencontainers.image.description="Base image with @bytelyst/* backend packages"
LABEL org.opencontainers.image.vendor="ByteLyst"
LABEL org.opencontainers.image.version="1.0.0"
# Install pnpm
RUN corepack enable && corepack prepare pnpm@10.6.5 --activate
WORKDIR /app
# Copy workspace files
COPY .npmrc .pnpmfile.cjs pnpm-workspace.yaml pnpm-lock.yaml* ./
# Copy packages from builder
COPY --from=builder /app/packages/ ./packages/
COPY --from=builder /app/packages/*/dist/ ./packages/*/dist/
# Install all @bytelyst/* packages (production only)
RUN pnpm install -r --filter './packages/*' --prod --ignore-scripts
# Verify installation
RUN pnpm list --depth=0 | grep @bytelyst
# Set default environment
ENV NODE_ENV=production
ENV BYTELYST_PACKAGE_SOURCE=common-plat
WORKDIR /app
```
**Build command:**
```bash
cd learning_ai_common_plat
docker build -f Dockerfile.backend-base \
-t bytelyst-common-base-backend:1.0.0 \
-t bytelyst-common-base-backend:latest \
-t bytelyst-common-base-backend:$(date +%Y%m%d) \
.
```
### 2.2 Web Base Image
**File:** `learning_ai_common_plat/Dockerfile.web-base`
```dockerfile
# ── Stage 1: Builder ─────────────────────────────────────────────────────
FROM node:22-slim AS builder
# Install pnpm
RUN corepack enable && corepack prepare pnpm@10.6.5 --activate
WORKDIR /app
# Copy workspace files
COPY .npmrc .pnpmfile.cjs pnpm-workspace.yaml pnpm-lock.yaml* ./
# Copy all packages
COPY packages/ ./packages/
# Build all @bytelyst/* packages
RUN pnpm -r --filter './packages/*' build
# ── Stage 2: Production Base ───────────────────────────────────────────────
FROM node:22-slim
# Metadata
LABEL org.opencontainers.image.title="ByteLyst Common Base - Web"
LABEL org.opencontainers.image.description="Base image with @bytelyst/* web packages"
LABEL org.opencontainers.image.vendor="ByteLyst"
LABEL org.opencontainers.image.version="1.0.0"
# Install pnpm
RUN corepack enable && corepack prepare pnpm@10.6.5 --activate
WORKDIR /app
# Copy workspace files
COPY .npmrc .pnpmfile.cjs pnpm-workspace.yaml pnpm-lock.yaml* ./
# Copy packages from builder
COPY --from=builder /app/packages/ ./packages/
COPY --from=builder /app/packages/*/dist/ ./packages/*/dist/
# Install all @bytelyst/* packages (production only)
RUN pnpm install -r --filter './packages/*' --prod --ignore-scripts
# Verify installation
RUN pnpm list --depth=0 | grep @bytelyst
# Set default environment
ENV NODE_ENV=production
ENV BYTELYST_PACKAGE_SOURCE=common-plat
ENV NEXT_TELEMETRY_DISABLED=1
WORKDIR /app
```
**Build command:**
```bash
cd learning_ai_common_plat
docker build -f Dockerfile.web-base \
-t bytelyst-common-base-web:1.0.0 \
-t bytelyst-common-base-web:latest \
-t bytelyst-common-base-web:$(date +%Y%m%d) \
.
```
### 2.3 Development Base Image (Optional)
For local development with devDependencies:
```dockerfile
# Dockerfile.dev-base
FROM node:22-slim
RUN corepack enable && corepack prepare pnpm@10.6.5 --activate
WORKDIR /app
COPY .npmrc .pnpmfile.cjs pnpm-workspace.yaml pnpm-lock.yaml* ./
COPY packages/ ./packages/
# Install with devDependencies
RUN pnpm install -r --filter './packages/*' --ignore-scripts
ENV NODE_ENV=development
```
---
## Part 3: Product Dockerfiles
### 3.1 Backend Dockerfile (Using Base Image)
**File:** `learning_ai_notes/backend/Dockerfile`
```dockerfile
# ── Stage 1: Build ───────────────────────────────────────────────────────
FROM bytelyst-common-base-backend:latest AS builder
WORKDIR /app/backend
# Copy backend package files
COPY backend/package.json ./package.json
COPY backend/tsconfig.json ./tsconfig.json
# Install backend-specific dependencies only
RUN pnpm install --prod --ignore-scripts
# Copy source code
COPY backend/src/ ./src/
COPY shared/ ../shared/
# Build backend
RUN pnpm run build
# ── Stage 2: Production ───────────────────────────────────────────────────
FROM bytelyst-common-base-backend:latest
WORKDIR /app/backend
# Copy backend package files
COPY backend/package.json ./package.json
# Install backend-specific dependencies
RUN pnpm install --prod --ignore-scripts
# Copy built artifacts from builder
COPY --from=builder /app/backend/dist ./dist
COPY --from=builder /app/backend/node_modules ./node_modules
COPY shared/ ../shared/
# Verify installation
RUN pnpm list --depth=0
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:4016/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
EXPOSE 4016
CMD ["node", "dist/server.js"]
```
### 3.2 Web Dockerfile (Using Base Image)
**File:** `learning_ai_notes/web/Dockerfile`
```dockerfile
# ── Stage 1: Build ───────────────────────────────────────────────────────
FROM bytelyst-common-base-web:latest AS builder
WORKDIR /app/web
# Copy web package files
COPY web/package.json ./package.json
COPY web/next.config.ts ./next.config.ts
COPY web/tsconfig.json ./tsconfig.json
COPY web/next-env.d.ts ./next-env.d.ts
# Install web-specific dependencies only
RUN pnpm install --prod --ignore-scripts
# Copy source code
COPY web/src/ ./src/
COPY web/public/ ./public/ # if exists
COPY shared/ ../shared/
# Build web
RUN pnpm run build
# ── Stage 2: Production ───────────────────────────────────────────────────
FROM bytelyst-common-base-web:latest
WORKDIR /app/web
# Copy web package files
COPY web/package.json ./package.json
# Install web-specific dependencies (production only)
RUN pnpm install --prod --ignore-scripts
# Copy built artifacts from builder
COPY --from=builder /app/web/.next/standalone ./
COPY --from=builder /app/web/.next/static ./.next/static
COPY shared/ ../shared/
# Environment
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
EXPOSE 3000
CMD ["node", "server.js"]
```
---
## Part 4: Build and Deployment Workflow
### 4.1 Base Image Build Workflow
**When to rebuild base image:**
1. When `@bytelyst/*` packages are updated
2. When Node.js version changes
3. When pnpm version changes
4. When security vulnerabilities are found in base layers
**Manual build:**
```bash
cd learning_ai_common_plat
# Build backend base
docker build -f Dockerfile.backend-base \
-t bytelyst-common-base-backend:1.0.0 \
-t bytelyst-common-base-backend:latest \
.
# Build web base
docker build -f Dockerfile.web-base \
-t bytelyst-common-base-web:1.0.0 \
-t bytelyst-common-base-web:latest \
.
```
**Push to registry (Gitea example):**
```bash
# Tag for Gitea registry
docker tag bytelyst-common-base-backend:latest \
gitea.example.com/bytelyst/common-base-backend:1.0.0
# Push
docker push gitea.example.com/bytelyst/common-base-backend:1.0.0
```
### 4.2 Automated Base Image Builds
**GitHub Actions workflow:**
```yaml
# .github/workflows/build-base-image.yml
name: Build Base Images
on:
push:
paths:
- 'packages/**'
- 'pnpm-lock.yaml'
workflow_dispatch:
jobs:
build-backend-base:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: corepack enable && corepack prepare pnpm@10.6.5 --activate
- run: pnpm install
- name: Build backend base image
run: |
docker build -f Dockerfile.backend-base \
-t bytelyst-common-base-backend:$(date +%Y%m%d) \
-t bytelyst-common-base-backend:latest \
.
- name: Push to registry
run: |
echo ${{ secrets.GITEA_TOKEN }} | docker login gitea.example.com -u username --password-stdin
docker push gitea.example.com/bytelyst/common-base-backend:latest
build-web-base:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: corepack enable && corepack prepare pnpm@10.6.5 --activate
- run: pnpm install
- name: Build web base image
run: |
docker build -f Dockerfile.web-base \
-t bytelyst-common-base-web:$(date +%Y%m%d) \
-t bytelyst-common-base-web:latest \
.
- name: Push to registry
run: |
echo ${{ secrets.GITEA_TOKEN }} | docker login gitea.example.com -u username --password-stdin
docker push gitea.example.com/bytelyst/common-base-web:latest
```
### 4.3 Product Image Build Workflow
**Build product image (using cached base):**
```bash
cd learning_ai_notes
# Base image is already pulled and cached
docker build -f backend/Dockerfile -t learning_ai_notes-backend:latest .
docker build -f web/Dockerfile -t learning_ai_notes-web:latest .
```
**Deployment script integration:**
```bash
#!/bin/bash
# deploy-notes.sh
# Ensure latest base image is pulled
docker pull bytelyst-common-base-backend:latest
docker pull bytelyst-common-base-web:latest
# Build product images (uses cached base)
docker compose build
# Deploy
docker compose up -d
```
---
## Part 5: Optimization Strategies
### 5.1 Layer Caching
**Order layers by change frequency:**
1. Base image layers (rarely change) - cached
2. package.json (changes when deps change) - rebuild
3. Source code (changes frequently) - rebuild
4. Build artifacts (changes with source) - rebuild
**Example:**
```dockerfile
# Good - source code after dependencies
COPY package.json ./
RUN pnpm install
COPY src/ ./src/
RUN pnpm run build
# Bad - source code before dependencies (invalidates cache)
COPY src/ ./src/
COPY package.json ./
RUN pnpm install
```
### 5.2 Multi-Stage Builds
**Minimize final image size:**
- Builder stage includes all build tools
- Production stage only includes runtime dependencies
- Copy only built artifacts, not source code
**Example:**
```dockerfile
# Builder stage - has TypeScript, devDependencies
FROM node:22-slim AS builder
RUN pnpm install
RUN pnpm run build
# Production stage - only runtime
FROM node:22-slim
COPY --from=builder /app/dist ./dist
# No TypeScript, no devDependencies
```
### 5.3 .dockerignore
**Exclude unnecessary files:**
```dockerignore
# Dependencies
node_modules
npm-debug.log
.pnpm-debug.log
# Build outputs
dist
.next
build
# Git
.git
.gitignore
# IDE
.vscode
.idea
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Tests
**/*.test.ts
**/*.spec.ts
**/__tests__/
coverage/
# Docs
README.md
docs/
*.md
# Misc
.env
.env.local
*.log
```
### 5.4 BuildKit Cache
**Use BuildKit for faster builds:**
```bash
# Enable BuildKit
export DOCKER_BUILDKIT=1
# Use cache mount for dependencies
docker build \
--mount type=cache,target=/root/.npm \
-f Dockerfile .
```
**In Dockerfile:**
```dockerfile
# Cache mount for pnpm store
RUN --mount=type=cache,target=/root/.local/share/pnpm/store \
pnpm install --prod
```
### 5.5 Parallel Builds
**Build backend and web in parallel:**
```bash
# Use docker compose
docker compose build
# Or manual parallel build
docker build -f backend/Dockerfile -t backend:latest . &
docker build -f web/Dockerfile -t web:latest . &
wait
```
---
## Part 6: Local Development
### 6.1 Development Without Docker
**Use .pnpmfile.cjs for local development:**
```javascript
// .pnpmfile.cjs
const PACKAGE_SOURCE = process.env.BYTELYST_PACKAGE_SOURCE || 'common-plat';
if (PACKAGE_SOURCE === 'common-plat') {
// Resolve from local common plat
// Works for local development without Docker
}
```
**Benefits:**
- Fast iteration (no Docker rebuild)
- Hot reload works
- Use local common plat changes immediately
- No need to rebuild base image
### 6.2 Development With Docker
**Use development base image:**
```dockerfile
FROM bytelyst-common-base-backend:dev AS dev
# Mount source code for hot reload
COPY backend/src/ ./src/
CMD ["pnpm", "run", "dev"]
```
**docker-compose.dev.yml:**
```yaml
services:
backend:
build:
target: dev
volumes:
- ./backend/src:/app/backend/src
environment:
- NODE_ENV=development
```
---
## Part 7: Troubleshooting
### 7.1 Base Image Not Found
**Error:**
```
ERROR: failed to solve: bytelyst-common-base-backend:latest: not found
```
**Solution:**
```bash
# Pull base image first
docker pull bytelyst-common-base-backend:latest
# Or build locally
cd learning_ai_common_plat
docker build -f Dockerfile.backend-base -t bytelyst-common-base-backend:latest .
```
### 7.2 Package Version Conflicts
**Error:**
```
ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies
```
**Solution:**
- Rebuild base image with updated packages
- Ensure product package.json versions align with base image
- Use `pnpm overrides` in product package.json if needed
### 7.3 Layer Cache Not Working
**Symptoms:**
- Builds are slow despite no changes
- All layers are being rebuilt
**Solution:**
```bash
# Clear Docker cache
docker builder prune -a
# Rebuild with BuildKit
export DOCKER_BUILDKIT=1
docker build -f Dockerfile .
```
### 7.4 Disk Space Issues
**Check disk usage:**
```bash
# Docker system disk usage
docker system df
# Image sizes
docker images
# Clean up unused images
docker image prune -a
# Clean up build cache
docker builder prune
```
---
## Part 8: Security Considerations
### 8.1 Base Image Scanning
**Scan for vulnerabilities:**
```bash
# Use Trivy
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image bytelyst-common-base-backend:latest
# Use Docker Scout
docker scout quickview bytelyst-common-base-backend:latest
docker scout cves bytelyst-common-base-backend:latest
```
### 8.2 Non-Root User
**Run as non-root in production:**
```dockerfile
# Create non-root user
RUN useradd -m -u 1000 appuser
# Change ownership
RUN chown -R appuser:appuser /app
# Switch to non-root
USER appuser
```
### 8.3 Minimal Base Image
**Use slim instead of alpine:**
- Alpine has glibc compatibility issues
- Slim is official Node.js variant
- Only slightly larger than alpine
**Example:**
```dockerfile
# Good - official slim
FROM node:22-slim
# Avoid - alpine has compatibility issues
FROM node:22-alpine
```
---
## Part 9: Monitoring and Maintenance
### 9.1 Base Image Version Tracking
**Track base image versions in products:**
```dockerfile
# Pin to specific version for reproducibility
FROM bytelyst-common-base-backend:1.0.0
# Update :latest after testing
# FROM bytelyst-common-base-backend:latest
```
**Audit product images for base version:**
```bash
# Check base image version
docker history learning_ai_notes-backend:latest | grep bytelyst-common-base
```
### 9.2 Update Workflow
**1. Update packages in common plat**
```bash
cd learning_ai_common_plat
# Update packages
pnpm update @bytelyst/*
```
**2. Rebuild base images**
```bash
docker build -f Dockerfile.backend-base -t bytelyst-common-base-backend:1.1.0 .
docker build -f Dockerfile.web-base -t bytelyst-common-base-web:1.1.0 .
```
**3. Test with one product**
```bash
cd learning_ai_notes
# Update Dockerfile to use new base
# FROM bytelyst-common-base-backend:1.1.0
docker build -f backend/Dockerfile .
docker compose up -d
# Test functionality
```
**4. Roll out to other products**
- Update Dockerfiles in other products
- Rebuild and deploy
- Monitor for issues
**5. Update :latest tag**
```bash
docker tag bytelyst-common-base-backend:1.1.0 bytelyst-common-base-backend:latest
docker push bytelyst-common-base-backend:latest
```
### 9.3 Rollback Strategy
**If base image update causes issues:**
```bash
# Revert product Dockerfile to previous base version
FROM bytelyst-common-base-backend:1.0.0 # Revert from 1.1.0
# Rebuild product
docker build -f backend/Dockerfile .
docker compose up -d
```
---
## Part 10: Migration Checklist
### For New Products
- [ ] Use `FROM bytelyst-common-base-backend:latest` in backend Dockerfile
- [ ] Use `FROM bytelyst-common-base-web:latest` in web Dockerfile
- [ ] Remove direct installation of `@bytelyst/*` packages
- [ ] Only install product-specific dependencies
- [ ] Add health checks
- [ ] Add metadata labels
- [ ] Update deployment script to pull base image
- [ ] Test build and deployment
- [ ] Document base image version in README
### For Existing Products
- [ ] Create new Dockerfile using base image (keep old as backup)
- [ ] Update deployment script to use new Dockerfile
- [ ] Test in staging environment
- [ ] Monitor for issues
- [ ] Deploy to production
- [ ] Remove old Dockerfile
- [ ] Document migration
---
## Part 11: CI/CD Integration
### 11.1 GitHub Actions Example
```yaml
# .github/workflows/deploy.yml
name: Deploy Product
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Pull base images
run: |
docker pull bytelyst-common-base-backend:latest
docker pull bytelyst-common-base-web:latest
- name: Build product images
run: |
docker compose build
- name: Deploy
run: |
docker compose up -d
- name: Health check
run: |
sleep 30
curl -f http://localhost:4016/health || exit 1
```
### 11.2 GitLab CI Example
```yaml
# .gitlab-ci.yml
deploy:
stage: deploy
image: docker:latest
services:
- docker:dind
script:
- docker pull bytelyst-common-base-backend:latest
- docker compose build
- docker compose up -d
only:
- main
```
---
## Appendix A: Quick Reference
### Build Commands
```bash
# Build backend base image
cd learning_ai_common_plat
docker build -f Dockerfile.backend-base -t bytelyst-common-base-backend:latest .
# Build web base image
docker build -f Dockerfile.web-base -t bytelyst-common-base-web:latest .
# Build product image
cd learning_ai_notes
docker build -f backend/Dockerfile -t learning_ai_notes-backend:latest .
# Build with docker compose
docker compose build
```
### Deploy Commands
```bash
# Pull latest base images
docker pull bytelyst-common-base-backend:latest
docker pull bytelyst-common-base-web:latest
# Build and deploy
docker compose build
docker compose up -d
# Check status
docker compose ps
docker compose logs
```
### Troubleshooting Commands
```bash
# Check image sizes
docker images
# Check disk usage
docker system df
# Clean up
docker system prune -a
docker builder prune -a
# Inspect image layers
docker history learning_ai_notes-backend:latest
# Check base image version
docker history learning_ai_notes-backend:latest | grep bytelyst-common-base
```
---
## Appendix B: File Structure
```
learning_ai_common_plat/
├── Dockerfile.backend-base # Backend base image definition
├── Dockerfile.web-base # Web base image definition
├── Dockerfile.dev-base # Development base image (optional)
└── packages/ # @bytelyst/* packages
learning_ai_notes/
├── backend/
│ └── Dockerfile # Uses bytelyst-common-base-backend:latest
├── web/
│ └── Dockerfile # Uses bytelyst-common-base-web:latest
└── docker-compose.yml
learning_ai_clock/
├── backend/
│ └── Dockerfile # Uses bytelyst-common-base-backend:latest
├── web/
│ └── Dockerfile # Uses bytelyst-common-base-web:latest
└── docker-compose.yml
```
---
## Appendix C: Environment Variables
### Base Image Variables
```bash
# Package source (for .pnpmfile.cjs)
BYTELYST_PACKAGE_SOURCE=common-plat # Use local packages (default)
BYTELYST_PACKAGE_SOURCE=vendor # Use vendor directory
BYTELYST_PACKAGE_SOURCE=gitea # Use Gitea registry
# Common plat root (for local development)
BYTELYST_COMMON_PLAT_ROOT=/path/to/learning_ai_common_plat
```
### Build Arguments
```dockerfile
# Dockerfile build arguments
ARG BYTELYST_PACKAGE_SOURCE=common-plat
ARG BYTELYST_COMMON_PLAT_ROOT
```
---
## Summary
This shared base image strategy provides:
- **53% disk space reduction** for 3 products (~400MB savings)
- **Faster builds** through layer caching
- **Consistent package versions** across all products
- **Simplified updates** - rebuild base, all products benefit
- **Better maintainability** - single source of truth for common packages
**Key benefits:**
- ✅ Minimal disk duplication
- ✅ Faster builds (base image cached)
- ✅ Easy to update packages
- ✅ Clean separation (common vs product code)
- ✅ Reusable across all current and future products
**Next steps:**
1. Create base image Dockerfiles in common plat repo
2. Build and test base images
3. Update product Dockerfiles to use base images
4. Update deployment scripts
5. Document and roll out to other products