Ctrl K
ComunidadGuías…

docker/DOCKER.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# ActaLog Docker Deployment Guide

This guide covers building, publishing, and deploying ActaLog using Docker containers with GitHub Container Registry (ghcr.io).

## Table of Contents

- [Overview](#overview)
- [Prerequisites](#prerequisites)
- [Quick Start](#quick-start)
- [Building Images](#building-images)
- [Publishing to GitHub Container Registry](#publishing-to-github-container-registry)
- [Automated Builds with GitHub Actions](#automated-builds-with-github-actions)
- [Rapid Testing with Development Builds](#rapid-testing-with-development-builds)
- [Production Deployment](#production-deployment)
- [Creating Stable Releases](#creating-stable-releases)
- [Troubleshooting](#troubleshooting)

---

## Overview

ActaLog uses a multi-stage Docker build that creates an optimized production image containing both the Go backend and Vue.js frontend. Images are published to GitHub Container Registry for easy distribution and deployment.

**Image Location:** `ghcr.io/OWNER/REPO:TAG`

**Key Features:**
- Multi-stage build (frontend, backend, runtime)
- Optimized Alpine-based runtime (< 50MB)
- Multi-architecture support (amd64, arm64)
- Automated builds on every push
- Build number tagging for rapid testing
- Semantic versioning for stable releases

---

## Prerequisites

### Local Development
- Docker 20.10+ with BuildKit
- Docker Compose 2.0+
- Git
- GitHub account with repository access

### GitHub Container Registry
- GitHub Personal Access Token (PAT) with `write:packages` permission
- Repository must be public OR you need `read:packages` on private repos

---

## Quick Start

### 1. Pull and Run Latest Image

```bash
# Pull latest image from GitHub Container Registry
docker pull ghcr.io/OWNER/REPO:latest

# Run with default SQLite database
docker run -d \
  --name actalog \
  -p 8080:8080 \
  -v actalog-data:/app/data \
  -v actalog-uploads:/app/uploads \
  -e JWT_SECRET=your_secure_secret_here \
  ghcr.io/OWNER/REPO:latest

# Access the application
open http://localhost:8080
```

### 2. Using Docker Compose (Recommended)

```bash
cd docker

# Copy and configure environment
cp .env.example .env
# Edit .env with your configuration

# Start the application
docker compose up -d

# View logs
docker compose logs -f

# Stop the application
docker compose down
```

---

## Building Images

### Using Build Script (Recommended)

The build script automatically extracts version information and creates properly tagged images.

```bash
# Build with default 'dev' tag
./docker/scripts/build.sh

# Build with custom tag
./docker/scripts/build.sh v0.9.0

# Build for specific platform
DOCKER_PLATFORM=linux/arm64 ./docker/scripts/build.sh

# Build for multiple platforms (requires buildx)
DOCKER_PLATFORM=linux/amd64,linux/arm64 ./docker/scripts/build.sh
```

**What the script does:**
1. Extracts version and build number from `pkg/version/version.go`
2. Builds multi-stage Docker image
3. Tags image with:
   - Custom tag (or 'dev')
   - Build number tag (`build-62`)
4. Loads image into local Docker daemon
5. Displays pull and push instructions

### Manual Build

```bash
# From project root
docker build \
  -f docker/Dockerfile \
  -t ghcr.io/OWNER/REPO:dev \
  .

# Multi-platform build
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -f docker/Dockerfile \
  -t ghcr.io/OWNER/REPO:dev \
  --push \
  .
```

---

## Publishing to GitHub Container Registry

### 1. Authenticate with GitHub

```bash
# Option 1: Using GitHub CLI (recommended)
gh auth login

# Option 2: Using Personal Access Token
export GITHUB_TOKEN=ghp_yourpersonalaccesstoken
echo $GITHUB_TOKEN | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin

# Option 3: Interactive login
docker login ghcr.io
# Username: YOUR_GITHUB_USERNAME
# Password: YOUR_PERSONAL_ACCESS_TOKEN
```

### 2. Push Image Using Script

```bash
# Push image with specific tag
./docker/scripts/push.sh dev

# Push stable release
./docker/scripts/push.sh v0.9.0
```

**What the script does:**
1. Checks GitHub Container Registry authentication
2. Pushes image with specified tag
3. Pushes build-specific tag (`build-62`)
4. Displays pull command and package URL

### 3. Manual Push

```bash
# Push specific tag
docker push ghcr.io/OWNER/REPO:dev

# Push multiple tags
docker push ghcr.io/OWNER/REPO:v0.9.0
docker push ghcr.io/OWNER/REPO:latest
```

### 4. Make Package Public (Optional)

After first push, packages are private by default.

1. Go to `https://github.com/OWNER/REPO/packages`
2. Click on `actalog` package
3. Go to **Package settings**
4. Scroll to **Danger Zone**
5. Click **Change visibility** � **Public**

---

## Automated Builds with GitHub Actions

### How It Works

The GitHub Actions workflow (`.github/workflows/docker-build.yml`) automatically builds and pushes Docker images on:

- **Push to `main` or `develop` branches** � builds and pushes
- **Push tags matching `v*`** � builds release with semantic versioning
- **Pull requests to `main`** � builds only (no push)
- **Manual workflow dispatch** � build with custom tag

### Automatic Tagging Strategy

Images are automatically tagged based on the event:

| Event | Tags Created | Example |
|-------|-------------|---------|
| Push to `main` | `latest`, `main-SHA`, `build-N` | `latest`, `main-a1b2c3d`, `build-42` |
| Push to `develop` | `develop`, `develop-SHA`, `build-N` | `develop`, `develop-x9y8z7w`, `build-43` |
| Tag `v1.2.3` | `v1.2.3`, `v1.2`, `v1`, `build-N` | `v1.2.3`, `v1.2`, `v1`, `build-44` |
| PR #123 | `pr-123` | `pr-123` |
| Manual trigger | Custom tag, `build-N` | `manual`, `build-45` |

### Required Secrets

**No secrets required!** The workflow uses the built-in `GITHUB_TOKEN` which has automatic access to GitHub Container Registry.

### Viewing Build Status

1. Go to **Actions** tab in GitHub
2. Click on **Build and Push Docker Image** workflow
3. View build summary with:
   - Build metadata
   - All tags created
   - Pull command

---

## Rapid Testing with Development Builds

### Problem: Testing Remote Deployments Quickly

You need to test changes on a remote server without creating formal releases.

### Solution: Build Number Tagging

Every build automatically gets a unique `build-N` tag based on GitHub Actions run number.

**Workflow:**

```bash
# 1. Push code to main/develop branch
git add .
git commit -m "feat: add new feature"
git push origin main

# 2. GitHub Actions automatically builds and tags:
#    - ghcr.io/OWNER/REPO:main-abc1234
#    - ghcr.io/OWNER/REPO:build-62
#    - ghcr.io/OWNER/REPO:latest

# 3. On remote server, pull latest build
ssh user@remote-server
docker pull ghcr.io/OWNER/REPO:build-62

# 4. Update docker-compose.yml or restart container
export TAG=build-62
docker compose up -d

# 5. Verify deployment
docker ps
curl http://localhost:8080/api/version
```

### Fast Iteration Cycle

```bash
# Local development
make build                    # Increments build number
git add . && git commit -m "..." && git push

# Watch GitHub Actions (2-5 minutes)
gh run watch

# Deploy on remote
ssh user@server "docker pull ghcr.io/OWNER/REPO:build-63 && docker compose up -d"

# Test
curl http://server:8080/api/version
# {"version":"0.9.0-beta","build":63,...}
```

### Automated Remote Deployment Script

Create `scripts/deploy-dev.sh`:

```bash
#!/bin/bash
set -e

SERVER="user@your-server.com"
BUILD_NUM=$(grep -E "^\s*Build\s*=\s*[0-9]+" pkg/version/version.go | awk '{print $3}')

echo "Deploying build ${BUILD_NUM} to ${SERVER}..."

# SSH and pull latest build
ssh $SERVER << EOF
  cd /opt/actalog
  export TAG=build-${BUILD_NUM}
  docker pull ghcr.io/OWNER/REPO:\$TAG
  docker compose up -d
  docker ps
  curl -s http://localhost:8080/api/version | jq .
EOF

echo "Deployment complete!"
```

**Usage:**
```bash
# Make changes
vim internal/handler/some_handler.go

# Build, commit, push
make build
git add . && git commit -m "fix: bug fix" && git push

# Wait for GitHub Actions, then deploy
gh run watch && ./scripts/deploy-dev.sh
```

---

## Production Deployment

### Using Docker Compose (Recommended)

**1. Create deployment directory:**
```bash
ssh user@production-server
mkdir -p /opt/actalog
cd /opt/actalog
```

**2. Copy docker-compose.yml and .env:**
```bash
# Download from repository
curl -O https://raw.githubusercontent.com/OWNER/REPO/main/docker/docker-compose.yml
curl -O https://raw.githubusercontent.com/OWNER/REPO/main/docker/.env.example
mv .env.example .env
```

**3. Configure environment:**
```bash
vim .env
```

**Required configuration:**
```env
GITHUB_OWNER=your-github-username
TAG=v0.9.0                    # Use stable release tag

# Change this!
JWT_SECRET=GENERATE_SECURE_RANDOM_STRING_HERE

# Optional: PostgreSQL instead of SQLite
DB_DRIVER=postgres
DB_HOST=postgres
DB_PORT=5432
DB_NAME=actalog
DB_USER=actalog
DB_PASSWORD=secure_password
```

**4. Start services:**
```bash
docker compose up -d
```

**5. Verify deployment:**
```bash
docker compose ps
docker compose logs -f
curl http://localhost:8080/health
curl http://localhost:8080/api/version
```

### Using Standalone Docker

```bash
docker run -d \
  --name actalog \
  --restart unless-stopped \
  -p 8080:8080 \
  -v actalog-data:/app/data \
  -v actalog-uploads:/app/uploads \
  -e DB_DRIVER=sqlite3 \
  -e DB_NAME=/app/data/actalog.db \
  -e JWT_SECRET=your_secure_secret_here \
  -e CORS_ORIGINS=https://yourdomain.com \
  ghcr.io/OWNER/REPO:v0.9.0
```

### Production with PostgreSQL

**docker-compose.yml:**
```yaml
version: '3.8'

services:
  actalog:
    image: ghcr.io/OWNER/REPO:v0.9.0
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - DB_DRIVER=postgres
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_NAME=actalog
      - DB_USER=actalog
      - DB_PASSWORD=${DB_PASSWORD}
      - JWT_SECRET=${JWT_SECRET}
    volumes:
      - actalog-uploads:/app/uploads
    depends_on:
      - postgres
    networks:
      - actalog-network

  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_DB=actalog
      - POSTGRES_USER=actalog
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - actalog-network

volumes:
  actalog-uploads:
  postgres-data:

networks:
  actalog-network:
```

---

## Creating Stable Releases

### Release Process

Stable releases use semantic versioning tags (`v1.2.3`) and are automatically built by GitHub Actions.

**1. Update version in code:**

Edit `pkg/version/version.go`:
```go
const (
    Major = 0
    Minor = 9
    Patch = 0
    PreRelease = "beta"  // or "" for stable
    Build = 62  // Auto-incremented
)
```

Edit `web/package.json`:
```json
{
  "version": "0.9.0"
}
```

**2. Update changelog:**

Edit `docs/CHANGELOG.md`:
```markdown
## [0.9.0-beta] - 2025-01-23

### Added
- Feature X
- Feature Y

### Fixed
- Bug Z
```

**3. Commit and push changes:**
```bash
git add pkg/version/version.go web/package.json docs/CHANGELOG.md
git commit -m "chore: bump version to v0.9.0-beta"
git push origin main
```

**4. Create and push Git tag:**
```bash
# Create annotated tag
git tag -a v0.9.0-beta -m "Release v0.9.0-beta

- Offline PWA support
- Docker deployment
- 1RM calculation
- Performance improvements"

# Push tag to GitHub
git push origin v0.9.0-beta
```

**5. GitHub Actions automatically:**
- Detects the tag
- Builds multi-platform images
- Tags images with:
  - `v0.9.0-beta`
  - `v0.9`
  - `v0`
  - `build-62`
- Pushes to GitHub Container Registry
- Creates build summary

**6. Create GitHub Release (optional):**

```bash
# Using GitHub CLI
gh release create v0.9.0-beta \
  --title "ActaLog v0.9.0-beta" \
  --notes-file docs/CHANGELOG.md \
  --prerelease

# Or manually at: https://github.com/OWNER/REPO/releases/new
```

**7. Verify release:**

```bash
# Pull release image
docker pull ghcr.io/OWNER/REPO:v0.9.0-beta

# Verify version
docker run --rm ghcr.io/OWNER/REPO:v0.9.0-beta /app/actalog --version
# ActaLog v0.9.0-beta+build.62
```

### Release Channels

| Channel | Tag Pattern | Use Case | Stability |
|---------|------------|----------|-----------|
| `latest` | Auto from `main` | Latest stable | Production-ready |
| `develop` | Auto from `develop` | Bleeding edge | Testing |
| `v1.2.3` | Git tag | Specific version | Production |
| `v1.2` | Auto from tag | Minor version | Production |
| `v1` | Auto from tag | Major version | Production |
| `build-N` | Every build | Rapid testing | Development |

### For End Users

**Stable Release (Recommended):**
```bash
docker pull ghcr.io/OWNER/REPO:v0.9.0
```

**Latest Stable:**
```bash
docker pull ghcr.io/OWNER/REPO:latest
```

**Specific Minor Version (auto-updates patches):**
```bash
docker pull ghcr.io/OWNER/REPO:v0.9
```

**Beta/Pre-release:**
```bash
docker pull ghcr.io/OWNER/REPO:v0.9.0-beta
```

---

## Troubleshooting

### Build Failures

**Problem:** Docker build fails with "npm ci" errors

**Solution:**
```bash
# Clear npm cache in Dockerfile or rebuild without cache
docker build --no-cache -f docker/Dockerfile .
```

**Problem:** Go build fails with "missing module"

**Solution:**
```bash
# Ensure go.mod and go.sum are up to date
go mod tidy
git add go.mod go.sum
git commit -m "chore: update go modules"
```

### Push Failures

**Problem:** "unauthorized: authentication required"

**Solution:**
```bash
# Re-authenticate
docker logout ghcr.io
echo $GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin
```

**Problem:** "denied: permission_denied"

**Solution:**
- Verify your GitHub token has `write:packages` permission
- Ensure you have push access to the repository
- Check package visibility settings

### Runtime Issues

**Problem:** Container starts but `/health` returns 404

**Solution:**
```bash
# Check logs
docker logs actalog

# Verify environment variables
docker exec actalog env | grep DB_

# Check if migrations ran
docker exec actalog ls -la /app/migrations
```

**Problem:** Database connection errors

**Solution:**
```bash
# For SQLite
docker exec actalog ls -la /app/data/

# For PostgreSQL
docker exec actalog ping postgres
docker compose logs postgres
```

### Connecting to Host Database (MariaDB/MySQL/PostgreSQL)

**Problem:** Container can't connect to database running on the host machine

```
dial tcp 127.0.0.1:3306: connect: connection refused
```

This happens because `127.0.0.1` inside a container refers to the container itself, not the host machine.

**Solution for Linux:**

1. Update `docker-compose.yml` to use `host.docker.internal`:

```yaml
services:
  actalog:
    image: ghcr.io/johnzastrow/actalog:latest
    extra_hosts:
      - "host.docker.internal:host-gateway"  # Required for Linux
    environment:
      - DB_DRIVER=mysql
      - DB_HOST=host.docker.internal  # NOT 127.0.0.1
      - DB_PORT=3306
      - DB_NAME=acta
      - DB_USER=acta
      - DB_PASSWORD=${DB_PASSWORD}
```

**Alternative:** Use Docker bridge IP (`172.17.0.1`) instead of `host.docker.internal`.

2. **Configure MariaDB/MySQL to accept connections from Docker:**

Check that MariaDB is listening on all interfaces (not just localhost):
```bash
sudo grep -E "bind-address|skip-networking" /etc/mysql/mariadb.conf.d/*.cnf
```

If `bind-address = 127.0.0.1`, change to `bind-address = 0.0.0.0` or comment it out, then restart MariaDB:
```bash
sudo systemctl restart mariadb
```

3. **Grant database user permission to connect from Docker network:**

```sql
-- For Docker bridge network (most secure)
GRANT ALL PRIVILEGES ON acta.* TO 'acta'@'172.17.%' IDENTIFIED BY 'your_password';
FLUSH PRIVILEGES;

-- Or allow from any host (less secure, but easier for testing)
GRANT ALL PRIVILEGES ON acta.* TO 'acta'@'%' IDENTIFIED BY 'your_password';
FLUSH PRIVILEGES;
```

4. **Verify connectivity:**

```bash
# Restart container
docker compose down && docker compose up -d

# Check logs
docker compose logs -f actalog

# Test from inside container
docker exec -it actalog sh -c "nc -zv host.docker.internal 3306"
```

**Note for Docker Desktop (Mac/Windows):** The `extra_hosts` directive is not needed—`host.docker.internal` works automatically.

5. **Check firewall rules (if connection times out):**

If the connection times out instead of being refused, a firewall may be blocking Docker's network:

```bash
# Check UFW status
sudo ufw status

# Allow MariaDB from Docker network
sudo ufw allow from 172.17.0.0/16 to any port 3306
sudo ufw reload

# For PostgreSQL
sudo ufw allow from 172.17.0.0/16 to any port 5432
sudo ufw reload
```

### Image Pull Issues

**Problem:** "pull access denied"

**Solution:**
```bash
# For private repositories, authenticate first
docker login ghcr.io

# Or make package public (see Publishing section)
```

**Problem:** "manifest unknown"

**Solution:**
```bash
# Verify tag exists
gh api /user/packages/container/actalog/versions

# Or check at: https://github.com/OWNER/REPO/pkgs/container/actalog
```

---

## Additional Resources

- [Docker Documentation](https://docs.docker.com/)
- [GitHub Container Registry Docs](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry)
- [GitHub Actions Docker Build](https://docs.docker.com/build/ci/github-actions/)
- [Docker Multi-platform Builds](https://docs.docker.com/build/building/multi-platform/)

---

## Summary Commands Cheat Sheet

```bash
# Build locally
./docker/scripts/build.sh v0.9.0

# Push to registry
./docker/scripts/push.sh v0.9.0

# Pull specific build for testing
docker pull ghcr.io/OWNER/REPO:build-62

# Deploy with Docker Compose
cd docker && docker compose up -d

# Create stable release
git tag -a v0.9.0 -m "Release v0.9.0"
git push origin v0.9.0

# Pull stable release
docker pull ghcr.io/OWNER/REPO:v0.9.0
```