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>
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 |
|
|
true |
|
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.mdKey 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
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 devand 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
============================================
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"
- 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
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:10MPORT- 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>