Files
website/docs/GITEA_DOCKER_WORKFLOW.md
matt ec72c5d62b
Some checks failed
build-website / build (push) Failing after 1m2s
Initial import of HarborSmith website
2025-09-18 22:20:01 +02:00

19 KiB

HarborSmith Gitea Docker Workflow

Automated Build & Deployment Pipeline

Version: 1.0
Date: September 2025
Purpose: Gitea repository setup with automated Docker image building and deployment


📋 Overview

This guide configures HarborSmith to work with your Gitea instance for:

  • Source control management
  • Automated Docker image building
  • Container registry management
  • Automated deployment to production

🏗️ Repository Structure

harborsmith/                    # Main repository in Gitea
├── .gitea/                    # Gitea-specific configuration
│   └── workflows/             # Gitea Actions (CI/CD)
│       ├── build.yaml         # Build Docker images on push
│       ├── test.yaml          # Run tests
│       └── deploy.yaml        # Deploy to production
├── .dockerignore              # Files to exclude from Docker builds
├── docker-compose.yml         # Production orchestration
├── docker-compose.dev.yml     # Development overrides
├── docker-compose.build.yml   # Build configuration
├── Makefile                   # Build and deploy commands
└── [rest of project structure as defined]

🔧 Gitea Actions Workflow

Build Workflow (.gitea/workflows/build.yaml)

name: Build and Push Docker Images
on:
  push:
    branches: [main, develop]
    tags:
      - 'v*'
  pull_request:
    branches: [main]

jobs:
  build-web:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      
      - name: Login to Gitea Registry
        uses: docker/login-action@v2
        with:
          registry: gitea.yourdomain.com
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: gitea.yourdomain.com/harborsmith/web
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}
      
      - name: Build and push Web
        uses: docker/build-push-action@v4
        with:
          context: ./apps/web
          file: ./apps/web/Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=registry,ref=gitea.yourdomain.com/harborsmith/web:buildcache
          cache-to: type=registry,ref=gitea.yourdomain.com/harborsmith/web:buildcache,mode=max
          build-args: |
            BUILDKIT_INLINE_CACHE=1
            NODE_ENV=production

  build-api:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      
      - name: Login to Gitea Registry
        uses: docker/login-action@v2
        with:
          registry: gitea.yourdomain.com
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: gitea.yourdomain.com/harborsmith/api
          
      - name: Build and push API
        uses: docker/build-push-action@v4
        with:
          context: ./apps/api
          file: ./apps/api/Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=registry,ref=gitea.yourdomain.com/harborsmith/api:buildcache
          cache-to: type=registry,ref=gitea.yourdomain.com/harborsmith/api:buildcache,mode=max

  build-services:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        service: [charter, maintenance, payments, notifications]
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      
      - name: Login to Gitea Registry
        uses: docker/login-action@v2
        with:
          registry: gitea.yourdomain.com
          username: ${{ secrets.REGISTRY_USERNAME }}
          password: ${{ secrets.REGISTRY_PASSWORD }}
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: gitea.yourdomain.com/harborsmith/${{ matrix.service }}
      
      - name: Build and push ${{ matrix.service }}
        uses: docker/build-push-action@v4
        with:
          context: ./services/${{ matrix.service }}
          file: ./services/${{ matrix.service }}/Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

🐳 Optimized Dockerfiles

Multi-stage Dockerfile for Next.js (apps/web/Dockerfile)

# Dependencies stage
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app

# Copy package files
COPY package.json pnpm-lock.yaml* ./
RUN corepack enable pnpm && pnpm i --frozen-lockfile

# Builder stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Build arguments for environment variables
ARG NEXT_PUBLIC_API_URL
ARG NEXT_PUBLIC_SUPABASE_URL
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
ARG NEXT_PUBLIC_STRIPE_PUBLIC_KEY
ARG NEXT_PUBLIC_KEYCLOAK_URL
ARG NEXT_PUBLIC_KEYCLOAK_REALM
ARG NEXT_PUBLIC_KEYCLOAK_CLIENT_ID

ENV NEXT_TELEMETRY_DISABLED 1
RUN corepack enable pnpm && pnpm build

# Runner stage
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

# Copy built application
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]

Optimized API Dockerfile (apps/api/Dockerfile)

# Build stage
FROM node:20-alpine AS builder
RUN apk add --no-cache libc6-compat
WORKDIR /app

COPY package.json pnpm-lock.yaml* ./
RUN corepack enable pnpm && pnpm i --frozen-lockfile

COPY . .
RUN pnpm build

# Production stage
FROM node:20-alpine AS runner
RUN apk add --no-cache libc6-compat
WORKDIR /app

ENV NODE_ENV production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 apiuser

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json

USER apiuser

EXPOSE 4000

CMD ["node", "dist/server.js"]

Microservice Dockerfile Template (services/*/Dockerfile)

FROM node:20-alpine AS builder
WORKDIR /app

COPY package*.json ./
COPY pnpm-lock.yaml* ./
RUN corepack enable pnpm && pnpm i --frozen-lockfile

COPY . .
RUN pnpm build

FROM node:20-alpine
WORKDIR /app

RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules

USER nodejs

EXPOSE 5001

CMD ["node", "dist/index.js"]

📦 Production docker-compose.yml (Using Gitea Registry)

version: '3.9'

networks:
  harborsmith:
    driver: bridge
    
volumes:
  postgres_data:
  minio_data:
  keycloak_data:
  redis_data:

services:
  # ===== FRONTEND =====
  web:
    image: gitea.yourdomain.com/harborsmith/web:latest
    container_name: harborsmith-web
    restart: unless-stopped
    ports:
      - "3000:3000"
    env_file:
      - .env.production
    depends_on:
      - api
      - keycloak
    networks:
      - harborsmith

  # ===== API GATEWAY =====
  api:
    image: gitea.yourdomain.com/harborsmith/api:latest
    container_name: harborsmith-api
    restart: unless-stopped
    ports:
      - "4000:4000"
    env_file:
      - .env.production
    depends_on:
      - postgres
      - redis
      - keycloak
    networks:
      - harborsmith

  # ===== MICROSERVICES =====
  charter-service:
    image: gitea.yourdomain.com/harborsmith/charter:latest
    container_name: harborsmith-charter
    restart: unless-stopped
    env_file:
      - .env.production
    depends_on:
      - postgres
      - redis
    networks:
      - harborsmith

  maintenance-service:
    image: gitea.yourdomain.com/harborsmith/maintenance:latest
    container_name: harborsmith-maintenance
    restart: unless-stopped
    env_file:
      - .env.production
    depends_on:
      - postgres
      - redis
    networks:
      - harborsmith

  payment-service:
    image: gitea.yourdomain.com/harborsmith/payments:latest
    container_name: harborsmith-payments
    restart: unless-stopped
    env_file:
      - .env.production
    depends_on:
      - postgres
    networks:
      - harborsmith

  notification-service:
    image: gitea.yourdomain.com/harborsmith/notifications:latest
    container_name: harborsmith-notifications
    restart: unless-stopped
    env_file:
      - .env.production
    networks:
      - harborsmith

  # ===== INFRASTRUCTURE SERVICES =====
  # (These use public images, not built from source)
  postgres:
    image: supabase/postgres:15.1.0.117
    container_name: harborsmith-db
    restart: unless-stopped
    ports:
      - "5432:5432"
    env_file:
      - .env.production
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./migrations:/docker-entrypoint-initdb.d:ro
    networks:
      - harborsmith

  redis:
    image: redis:7-alpine
    container_name: harborsmith-redis
    restart: unless-stopped
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    networks:
      - harborsmith

  keycloak:
    image: quay.io/keycloak/keycloak:23.0
    container_name: harborsmith-keycloak
    restart: unless-stopped
    ports:
      - "8080:8080"
    env_file:
      - .env.production
    volumes:
      - keycloak_data:/opt/keycloak/data
    depends_on:
      - postgres
    networks:
      - harborsmith

  minio:
    image: minio/minio:latest
    container_name: harborsmith-minio
    restart: unless-stopped
    ports:
      - "9000:9000"
      - "9001:9001"
    env_file:
      - .env.production
    volumes:
      - minio_data:/data
    command: server /data --console-address ":9001"
    networks:
      - harborsmith

  # Additional services as defined in main implementation plan...

🔄 Deployment Workflow

1. Initial Setup on Production Server

# On your production server
# 1. Install Docker and Docker Compose
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
apt-get install docker-compose-plugin

# 2. Create project directory
mkdir -p /opt/harborsmith
cd /opt/harborsmith

# 3. Login to Gitea Registry
docker login gitea.yourdomain.com

# 4. Clone deployment files (not full source)
git clone https://gitea.yourdomain.com/harborsmith/deployment.git .

2. Deployment Script (deploy.sh)

#!/bin/bash
# HarborSmith Deployment Script

set -e

echo "🚀 Starting HarborSmith deployment..."

# Configuration
GITEA_REGISTRY="gitea.yourdomain.com"
PROJECT="harborsmith"
ENVIRONMENT=${1:-production}

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# Function to print colored output
print_status() {
    echo -e "${GREEN}${NC} $1"
}

print_error() {
    echo -e "${RED}${NC} $1"
}

print_warning() {
    echo -e "${YELLOW}${NC} $1"
}

# 1. Login to Gitea Registry
print_status "Logging into Gitea Registry..."
docker login ${GITEA_REGISTRY}

# 2. Pull latest images
print_status "Pulling latest images..."
SERVICES="web api charter maintenance payments notifications"
for SERVICE in $SERVICES; do
    print_status "Pulling ${SERVICE}..."
    docker pull ${GITEA_REGISTRY}/${PROJECT}/${SERVICE}:latest
done

# 3. Backup database (optional but recommended)
if [ "$ENVIRONMENT" = "production" ]; then
    print_warning "Backing up database..."
    docker exec harborsmith-db pg_dump -U postgres harborsmith > backup-$(date +%Y%m%d-%H%M%S).sql
fi

# 4. Stop running containers
print_status "Stopping current containers..."
docker-compose down

# 5. Start new containers
print_status "Starting new containers..."
docker-compose up -d

# 6. Run migrations
print_status "Running database migrations..."
docker exec harborsmith-db psql -U postgres -d harborsmith -f /docker-entrypoint-initdb.d/migrate.sql

# 7. Health checks
print_status "Performing health checks..."
sleep 10

# Check if services are running
HEALTH_CHECK_FAILED=0
for SERVICE in $SERVICES; do
    if docker ps | grep -q "harborsmith-${SERVICE}"; then
        print_status "${SERVICE} is running"
    else
        print_error "${SERVICE} is not running"
        HEALTH_CHECK_FAILED=1
    fi
done

# 8. Cleanup old images
print_status "Cleaning up old images..."
docker image prune -f

if [ $HEALTH_CHECK_FAILED -eq 0 ]; then
    print_status "Deployment completed successfully! 🎉"
    echo ""
    echo "Services available at:"
    echo "  - Web: http://localhost:3000"
    echo "  - API: http://localhost:4000"
    echo "  - Keycloak: http://localhost:8080"
    echo "  - MinIO: http://localhost:9001"
else
    print_error "Deployment completed with errors. Please check the logs."
    exit 1
fi

🛠️ Makefile for Common Operations

# HarborSmith Makefile
.PHONY: help build push deploy logs restart clean

# Variables
GITEA_REGISTRY := gitea.yourdomain.com
PROJECT := harborsmith
VERSION := $(shell git describe --tags --always --dirty)
BRANCH := $(shell git rev-parse --abbrev-ref HEAD)

help: ## Show this help
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

build: ## Build all Docker images locally
	docker-compose -f docker-compose.build.yml build

build-web: ## Build web service
	docker build -t $(GITEA_REGISTRY)/$(PROJECT)/web:$(VERSION) ./apps/web

build-api: ## Build API service
	docker build -t $(GITEA_REGISTRY)/$(PROJECT)/api:$(VERSION) ./apps/api

push: ## Push all images to Gitea registry
	docker push $(GITEA_REGISTRY)/$(PROJECT)/web:$(VERSION)
	docker push $(GITEA_REGISTRY)/$(PROJECT)/api:$(VERSION)
	docker push $(GITEA_REGISTRY)/$(PROJECT)/charter:$(VERSION)
	docker push $(GITEA_REGISTRY)/$(PROJECT)/maintenance:$(VERSION)
	docker push $(GITEA_REGISTRY)/$(PROJECT)/payments:$(VERSION)
	docker push $(GITEA_REGISTRY)/$(PROJECT)/notifications:$(VERSION)

deploy: ## Deploy to production
	ssh production-server 'cd /opt/harborsmith && ./deploy.sh'

deploy-staging: ## Deploy to staging
	ssh staging-server 'cd /opt/harborsmith && ./deploy.sh staging'

logs: ## Show logs from all services
	docker-compose logs -f

logs-web: ## Show logs from web service
	docker-compose logs -f web

restart: ## Restart all services
	docker-compose restart

restart-web: ## Restart web service
	docker-compose restart web

clean: ## Clean up Docker resources
	docker system prune -f
	docker volume prune -f

backup: ## Backup database
	docker exec harborsmith-db pg_dump -U postgres harborsmith > backups/backup-$(shell date +%Y%m%d-%H%M%S).sql

restore: ## Restore database from latest backup
	docker exec -i harborsmith-db psql -U postgres harborsmith < $(shell ls -t backups/*.sql | head -1)

dev: ## Start development environment
	docker-compose -f docker-compose.dev.yml up

test: ## Run tests in containers
	docker-compose -f docker-compose.test.yml up --abort-on-container-exit

migrate: ## Run database migrations
	docker exec harborsmith-db psql -U postgres -d harborsmith -f /docker-entrypoint-initdb.d/migrate.sql

🔐 Gitea Repository Secrets

Configure these secrets in your Gitea repository settings:

REGISTRY_USERNAME: harborsmith-bot
REGISTRY_PASSWORD: [secure-token]
PRODUCTION_HOST: production.harborsmith.com
STAGING_HOST: staging.harborsmith.com
SSH_PRIVATE_KEY: [deployment-key]

📝 .dockerignore

# Dependencies
node_modules
npm-debug.log
yarn-error.log
.pnpm-store

# Next.js
.next
out
build
dist

# Testing
coverage
.nyc_output

# Environment
.env
.env.*
!.env.example

# Git
.git
.gitignore
.gitea

# Documentation
*.md
docs

# IDE
.vscode
.idea
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Development
.docker
docker-compose.dev.yml
docker-compose.test.yml

# Temporary files
tmp
temp
*.tmp

🚀 Complete Workflow Example

1. Developer pushes code to Gitea

git add .
git commit -m "feat: add vessel booking feature"
git push origin develop

2. Gitea Actions automatically:

  • Runs tests
  • Builds Docker images
  • Tags with branch name and commit SHA
  • Pushes to Gitea container registry

3. Deploy to staging

make deploy-staging
# OR manually:
ssh staging-server
cd /opt/harborsmith
docker-compose pull
docker-compose up -d

4. Deploy to production (after testing)

git checkout main
git merge develop
git tag v1.2.0
git push origin main --tags

# Gitea Actions builds production images
make deploy

📊 Monitoring Container Health

Health Check Script (healthcheck.sh)

#!/bin/bash
# Container health monitoring

SERVICES=("web" "api" "charter" "maintenance" "payments" "notifications")
WEBHOOK_URL="https://your-monitoring-webhook.com"

for service in "${SERVICES[@]}"; do
    if ! docker exec harborsmith-${service} curl -f http://localhost:${PORT}/health > /dev/null 2>&1; then
        curl -X POST ${WEBHOOK_URL} \
            -H "Content-Type: application/json" \
            -d "{\"service\": \"${service}\", \"status\": \"unhealthy\", \"timestamp\": \"$(date -Iseconds)\"}"
        
        # Attempt restart
        docker-compose restart ${service}
    fi
done

🔄 Rolling Updates

For zero-downtime deployments:

# docker-compose.production.yml
services:
  web:
    image: gitea.yourdomain.com/harborsmith/web:latest
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3

Deployment Checklist

  • Gitea repository created
  • Container registry enabled in Gitea
  • Gitea Actions configured
  • Secrets added to repository
  • Production server has Docker installed
  • Registry credentials configured on server
  • Environment files prepared
  • SSL certificates ready
  • Database backups scheduled
  • Monitoring configured
  • Rollback plan documented

🆘 Troubleshooting

Common Issues

  1. Image pull authentication failed
docker logout gitea.yourdomain.com
docker login gitea.yourdomain.com
  1. Container fails to start
docker logs harborsmith-web
docker-compose down
docker-compose up -d
  1. Database connection issues
docker exec harborsmith-db pg_isready
docker-compose restart postgres
  1. Build cache issues
docker builder prune -a
docker-compose build --no-cache

This workflow ensures automated, reliable deployments from your Gitea repository to production Docker containers.