Files
Thomas Richter 80e4769221 docs(06): create phase plan
Phase 06: Deployment
- 2 plans in 2 waves
- Wave 1: Docker configuration (adapter-node, Dockerfile, docker-compose)
- Wave 2: Runtime configuration (health endpoint, env docs, backup script)
- Ready for execution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 13:06:33 +01:00

11 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
phase plan type wave depends_on files_modified autonomous must_haves
06-deployment 02 execute 2
06-01
src/routes/health/+server.ts
.env.example
backup.sh
README.md
true
truths artifacts key_links
Health endpoint returns 200 when database is accessible
Health endpoint returns 503 when database fails
Environment variables are documented with examples
Backup script creates timestamped archive of data volume
path provides exports
src/routes/health/+server.ts Health check endpoint for Docker
GET
path provides contains
.env.example Environment variable documentation TASKPLANER_DATA_DIR
path provides contains
backup.sh Volume backup script tar czf
from to via pattern
Dockerfile src/routes/health/+server.ts HEALTHCHECK wget command /health
from to via pattern
docker-compose.yml .env.example environment variable reference ORIGIN
Runtime configuration with health checks, environment documentation, and backup tooling

Purpose: Complete the production deployment setup with health monitoring for Docker, clear documentation of configuration options, and a backup script for data preservation.

Output: /health endpoint, .env.example template, backup.sh script, updated README with Docker instructions

<execution_context> @/home/tho/.claude/get-shit-done/workflows/execute-plan.md @/home/tho/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/06-deployment/06-RESEARCH.md @.planning/phases/06-deployment/06-01-SUMMARY.md

Key from Plan 01:

  • Dockerfile has HEALTHCHECK pointing to /health
  • Data paths use TASKPLANER_DATA_DIR env var
  • docker-compose.yml uses named volume taskplaner_data
Task 1: Create health check endpoint src/routes/health/+server.ts Create src/routes/health/+server.ts:
import type { RequestHandler } from './$types';
import { db } from '$lib/server/db';
import { entries } from '$lib/server/db/schema';

export const GET: RequestHandler = async () => {
  try {
    // Verify database connectivity with a simple query
    db.select().from(entries).limit(1).all();

    return new Response('ok', {
      status: 200,
      headers: { 'Content-Type': 'text/plain' }
    });
  } catch (error) {
    console.error('Health check failed:', error);

    return new Response('unhealthy', {
      status: 503,
      headers: { 'Content-Type': 'text/plain' }
    });
  }
};

The endpoint:

  • Returns 200 "ok" when database is accessible
  • Returns 503 "unhealthy" when database query fails
  • Logs errors for debugging but doesn't expose details
  • Uses simple text response (Docker just needs status code)
    • npm run dev and visit http://localhost:5173/health returns "ok"
    • Health endpoint returns 200 status code
    • Database query is executed (visible in dev server logs on first request) /health endpoint returns 200 with database connectivity check
Task 2: Create environment documentation and backup script .env.example, backup.sh 1. Create .env.example: ```bash # TaskPlaner Environment Configuration # Copy to .env and customize for your deployment

============================================

Server Configuration

============================================

Port the server listens on (inside container)

Map to host port via docker-compose ports setting

PORT=3000

============================================

Data Storage

============================================

Directory for database and uploads

Docker: /data (must match volume mount)

Local development: ./data

TASKPLANER_DATA_DIR=/data

Optional: Direct database path override

DATABASE_PATH=/data/db/taskplaner.db

============================================

Production URL (REQUIRED for production)

============================================

The full URL where users access the app

Used for CSRF validation and generating absolute URLs

Example: https://tasks.example.com

ORIGIN=http://localhost:3000

============================================

Request Limits

============================================

Maximum request body size

Supports K, M, G suffixes

Default: 512kb, recommended for images: 10M

BODY_SIZE_LIMIT=10M

============================================

Reverse Proxy Configuration

Uncomment when running behind nginx/traefik/etc

============================================

Header containing original protocol (http/https)

PROTOCOL_HEADER=x-forwarded-proto

Header containing original host

HOST_HEADER=x-forwarded-host

Header containing original client IP

ADDRESS_HEADER=x-forwarded-for

Number of trusted proxies in front of app

XFF_DEPTH=1


2. Create backup.sh:
```bash
#!/bin/bash
# TaskPlaner Data Backup Script
# Creates a timestamped backup of the Docker volume

set -e

# Configuration (override via environment)
BACKUP_DIR="${BACKUP_DIR:-./backups}"
VOLUME_NAME="${VOLUME_NAME:-taskplaner_taskplaner_data}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/taskplaner_backup_${TIMESTAMP}.tar.gz"

# Create backup directory if needed
mkdir -p "$BACKUP_DIR"

echo "========================================="
echo "TaskPlaner Backup"
echo "========================================="
echo "Volume: $VOLUME_NAME"
echo "Output: $BACKUP_FILE"
echo ""

# Check if volume exists
if ! docker volume inspect "$VOLUME_NAME" > /dev/null 2>&1; then
  echo "Error: Volume '$VOLUME_NAME' not found"
  echo ""
  echo "Available volumes:"
  docker volume ls --format '  - {{.Name}}' | grep -i taskplaner || echo "  (none with 'taskplaner' in name)"
  echo ""
  echo "Tip: Set VOLUME_NAME environment variable to use a different volume"
  exit 1
fi

# Create backup using temporary Alpine container
echo "Creating backup..."
docker run --rm \
  -v "${VOLUME_NAME}:/data:ro" \
  -v "$(cd "$BACKUP_DIR" && pwd):/backup" \
  alpine:latest \
  tar czf "/backup/taskplaner_backup_${TIMESTAMP}.tar.gz" -C /data .

echo ""
echo "Backup complete!"
echo "File: $BACKUP_FILE"
echo "Size: $(du -h "$BACKUP_FILE" | cut -f1)"
echo ""
echo "To restore: docker run --rm -v ${VOLUME_NAME}:/data -v \$(pwd)/${BACKUP_DIR}:/backup alpine tar xzf /backup/taskplaner_backup_${TIMESTAMP}.tar.gz -C /data"
  1. Make backup.sh executable: After creating the file, run: chmod +x backup.sh
    • .env.example exists with all documented variables
    • backup.sh exists and is executable: ls -la backup.sh
    • backup.sh syntax is valid: bash -n backup.sh .env.example documents all configuration options, backup.sh creates timestamped archive
Task 3: Add Docker deployment section to README README.md Check if README.md exists. If it does, add a Docker Deployment section. If not, create a minimal README with the Docker section.

Add this section (after any existing content, or as the main content):

## Docker Deployment

### Quick Start

```bash
# Build and start the container
docker-compose up -d

# View logs
docker-compose logs -f

# Stop the container
docker-compose down

The application will be available at http://localhost:3000

Configuration

Copy .env.example to .env and customize:

cp .env.example .env

Key settings:

  • ORIGIN - Required for production. Set to your public URL (e.g., https://tasks.example.com)
  • BODY_SIZE_LIMIT - Max upload size. Default: 512kb, recommended: 10M
  • PORT - Server port inside container. Default: 3000

Behind a Reverse Proxy

When running behind nginx, traefik, or similar, uncomment these in .env:

PROTOCOL_HEADER=x-forwarded-proto
HOST_HEADER=x-forwarded-host
ADDRESS_HEADER=x-forwarded-for
XFF_DEPTH=1

Data Persistence

Data is stored in a Docker named volume (taskplaner_data). This includes:

  • SQLite database (/data/db/taskplaner.db)
  • Uploaded images (/data/uploads/)

The volume persists across container restarts and updates.

Backup & Restore

Create a backup:

./backup.sh

Backups are saved to ./backups/ with timestamps.

Restore from backup:

# Stop the container first
docker-compose down

# Restore (replace TIMESTAMP with actual backup filename)
docker run --rm \
  -v taskplaner_taskplaner_data:/data \
  -v $(pwd)/backups:/backup \
  alpine tar xzf /backup/taskplaner_backup_TIMESTAMP.tar.gz -C /data

# Start the container
docker-compose up -d

Health Check

The container includes a health check at /health. View status:

docker-compose ps

A healthy container shows (healthy) in the status column.


If README.md exists, preserve existing content and add this as a new section.
If README.md doesn't exist, create it with a title "# TaskPlaner" followed by this Docker section.
  </action>
  <verify>
    - README.md exists with Docker Deployment section
    - Section includes Quick Start, Configuration, Backup instructions
    - Commands are correct and copy-pasteable
  </verify>
  <done>README.md documents Docker deployment with quick start, configuration, and backup instructions</done>
</task>

</tasks>

<verification>
1. Health endpoint works: `curl http://localhost:5173/health` returns "ok" (dev mode)
2. Environment documented: `.env.example` has all configuration options
3. Backup script works: `./backup.sh` (requires running container with volume)
4. README complete: Docker section with quick start and backup instructions

Full Docker verification (after both plans):
```bash
# Build and start
docker-compose up -d

# Check health
docker-compose ps  # Should show (healthy)
curl http://localhost:3000/health  # Should return "ok"

# Verify data persistence
# Add an entry via the UI, then:
docker-compose down
docker-compose up -d
# Entry should still exist

# Test backup
./backup.sh
ls -la backups/  # Should show timestamped tar.gz

<success_criteria>

  • /health endpoint returns 200 when database accessible, 503 on failure
  • .env.example documents PORT, TASKPLANER_DATA_DIR, ORIGIN, BODY_SIZE_LIMIT, proxy headers
  • backup.sh creates timestamped tar.gz of data volume
  • README.md has Docker deployment section with quick start and backup instructions
  • Container shows "healthy" status in docker-compose ps </success_criteria>
After completion, create `.planning/phases/06-deployment/06-02-SUMMARY.md`