From 7feb34798737a3903934f120888f6e8b6ce7bd72 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Nov 2025 21:03:57 +0000 Subject: [PATCH 01/12] feat: Add Peekaping monitoring service infrastructure Add complete infrastructure setup for Peekaping as an alternative to Uptime Kuma. Includes Terraform configuration for Hetzner VM provisioning, Ansible playbook for automated deployment, and Docker Compose setup for local development and testing. Key components: - Terraform module for Peekaping service (CX23 server, 10GB volume) - Ansible playbook with microservice Docker Compose deployment - Local development environment with docker-compose - Makefile targets for deployment (make deploy-peekaping) - Comprehensive documentation and configuration examples The service runs in microservice mode with Redis, API, Producer, Worker, Ingester, Web, and Gateway components. Uptime Kuma configuration remains unchanged. --- .gitignore | 5 + Makefile | 15 + ansible/playbooks/peekaping.yaml | 336 ++++++++++++++++++++++ docker/peekaping/.env.example | 24 ++ docker/peekaping/README.md | 81 ++++++ docker/peekaping/docker-compose.yml | 126 ++++++++ docker/peekaping/nginx.conf | 30 ++ terraform/base/main.tf | 8 + terraform/services/peekaping/README.md | 280 ++++++++++++++++++ terraform/services/peekaping/main.tf | 70 +++++ terraform/services/peekaping/outputs.tf | 35 +++ terraform/services/peekaping/variables.tf | 53 ++++ 12 files changed, 1063 insertions(+) create mode 100644 ansible/playbooks/peekaping.yaml create mode 100644 docker/peekaping/.env.example create mode 100644 docker/peekaping/README.md create mode 100644 docker/peekaping/docker-compose.yml create mode 100644 docker/peekaping/nginx.conf create mode 100644 terraform/services/peekaping/README.md create mode 100644 terraform/services/peekaping/main.tf create mode 100644 terraform/services/peekaping/outputs.tf create mode 100644 terraform/services/peekaping/variables.tf diff --git a/.gitignore b/.gitignore index 536d051..0d6808e 100644 --- a/.gitignore +++ b/.gitignore @@ -136,6 +136,11 @@ repopack.txt **/*.retry **/ansible.log **/*.vault +**/inventory-*.ini # Environment files .envrc + +# Docker local data +**/docker/**/.data/ +**/docker/**/.env diff --git a/Makefile b/Makefile index fc77484..e50f6e3 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,12 @@ ssh-key: # Save SSH private key to file @echo "SSH key saved to uptime-kuma-key.pem" .PHONY: ssh-key +ssh-key-peekaping: # Save Peekaping SSH private key to file + terraform -chdir=$(TF_DIR) output -raw module.peekaping.ssh_private_key > peekaping-key.pem + chmod 600 peekaping-key.pem + @echo "SSH key saved to peekaping-key.pem" +.PHONY: ssh-key-peekaping + deploy-uptime: init-vendor # Deploy or update Uptime Kuma using Ansible @echo "Deploying Uptime Kuma..." @if [ ! -f ansible/inventory-uptime-kuma.ini ]; then \ @@ -86,6 +92,15 @@ deploy-uptime: init-vendor # Deploy or update Uptime Kuma using Ansible cd ansible && ansible-playbook -i inventory-uptime-kuma.ini playbooks/uptime-kuma.yaml .PHONY: deploy-uptime +deploy-peekaping: init-vendor # Deploy or update Peekaping using Ansible + @echo "Deploying Peekaping..." + @if [ ! -f ansible/inventory-peekaping.ini ]; then \ + echo "Error: Ansible inventory file not found. Run 'make apply' first to generate it."; \ + exit 1; \ + fi + cd ansible && ansible-playbook -i inventory-peekaping.ini playbooks/peekaping.yaml +.PHONY: deploy-peekaping + todo: # Show to-do items per file $(Q) grep \ --exclude=Makefile.util \ diff --git a/ansible/playbooks/peekaping.yaml b/ansible/playbooks/peekaping.yaml new file mode 100644 index 0000000..41cfe04 --- /dev/null +++ b/ansible/playbooks/peekaping.yaml @@ -0,0 +1,336 @@ +--- +- name: Deploy Peekaping with Docker and Nginx + hosts: all + become: true + vars: + # Variables to be passed via Terraform/cloud-init + # domain: peekaping.ainsley.dev + # admin_email: hello@ainsley.dev + docker_port: 8383 + service_name: peekaping + data_mount_point: /mnt/peekaping + enable_https: true + + pre_tasks: + - name: Update apt package cache + apt: + update_cache: yes + cache_valid_time: 3600 + + - name: Upgrade all packages + apt: + upgrade: dist + register: upgrade_result + + - name: Ensure python3-pip is installed + apt: + name: python3-pip + state: present + + - name: Reboot if kernel updated + reboot: + msg: 'Reboot initiated by Ansible due to package upgrade' + reboot_timeout: 600 + when: + - upgrade_result.changed + - skip_reboot is not defined or not skip_reboot + + roles: + - fail2ban + - docker + - nginx + - ufw + + tasks: + - name: Check if Hetzner volume is attached + stat: + path: "{{ data_mount_point }}" + register: volume_mount + + - name: Create data mount point directory + file: + path: "{{ data_mount_point }}" + state: directory + mode: '0755' + when: not volume_mount.stat.exists + + - name: Find Hetzner volume device + shell: | + lsblk -o NAME,SERIAL,MOUNTPOINT | grep -E "HC_Volume" | head -1 | awk '{print "/dev/" $1}' + register: volume_device + changed_when: false + failed_when: false + + - name: Format Hetzner volume if not already formatted + filesystem: + fstype: ext4 + dev: "{{ volume_device.stdout }}" + when: + - volume_device.stdout != "" + - not volume_mount.stat.exists + ignore_errors: yes + + - name: Mount Hetzner volume + mount: + path: "{{ data_mount_point }}" + src: "{{ volume_device.stdout }}" + fstype: ext4 + state: mounted + opts: defaults,nofail + when: volume_device.stdout != "" + ignore_errors: yes + + - name: Ensure mount point has correct permissions + file: + path: "{{ data_mount_point }}" + state: directory + mode: '0755' + owner: root + group: root + + - name: Create Peekaping deployment directory + file: + path: /opt/peekaping + state: directory + mode: '0755' + + - name: Copy docker-compose.yml to server + copy: + content: | + version: '3.8' + + networks: + appnet: + driver: bridge + + services: + redis: + image: redis:7 + restart: unless-stopped + networks: + - appnet + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 30s + timeout: 2s + retries: 5 + start_period: 5s + + migrate: + image: 0xfurai/peekaping-migrate:latest + restart: "no" + env_file: + - .env + volumes: + - {{ data_mount_point }}:/app/data + + api: + image: 0xfurai/peekaping-api:latest + restart: unless-stopped + env_file: + - .env + volumes: + - {{ data_mount_point }}:/app/data + depends_on: + redis: + condition: service_started + migrate: + condition: service_completed_successfully + networks: + - appnet + healthcheck: + test: + [ + "CMD-SHELL", + "wget -qO - http://localhost:8034/api/v1/health || exit 1", + ] + interval: 30s + timeout: 2s + retries: 5 + start_period: 5s + + producer: + image: 0xfurai/peekaping-producer:latest + restart: unless-stopped + env_file: + - .env + volumes: + - {{ data_mount_point }}:/app/data + depends_on: + redis: + condition: service_healthy + migrate: + condition: service_completed_successfully + networks: + - appnet + + worker: + image: 0xfurai/peekaping-worker:latest + restart: unless-stopped + env_file: + - .env + depends_on: + redis: + condition: service_healthy + networks: + - appnet + + ingester: + image: 0xfurai/peekaping-ingester:latest + restart: unless-stopped + env_file: + - .env + volumes: + - {{ data_mount_point }}:/app/data + depends_on: + redis: + condition: service_started + migrate: + condition: service_completed_successfully + networks: + - appnet + + web: + image: 0xfurai/peekaping-web:latest + depends_on: + api: + condition: service_healthy + networks: + - appnet + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"] + interval: 30s + timeout: 2s + retries: 5 + start_period: 5s + + gateway: + image: nginx:latest + ports: + - "{{ docker_port }}:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + depends_on: + api: + condition: service_healthy + web: + condition: service_healthy + networks: + - appnet + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"] + interval: 30s + timeout: 2s + retries: 5 + start_period: 5s + dest: /opt/peekaping/docker-compose.yml + mode: '0644' + + - name: Copy nginx.conf to server + copy: + content: | + events {} + http { + upstream api { server api:8034; } + upstream web { server web:80; } + + server { + listen 80; + + # Pure API calls + location /api/ { + proxy_pass http://api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # socket.io + location /socket.io/ { + proxy_pass http://api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # Everything else → static SPA + location / { + proxy_pass http://web; + } + } + } + dest: /opt/peekaping/nginx.conf + mode: '0644' + + - name: Create .env file for Peekaping + copy: + content: | + # Database Configuration + DB_USER=root + DB_PASS={{ lookup('password', '/tmp/peekaping_db_pass chars=ascii_letters,digits length=32') }} + DB_NAME=/app/data/peekaping.db + DB_TYPE=sqlite + + # Server Configuration + SERVER_PORT=8034 + CLIENT_URL="https://{{ domain }}" + + # Application Settings + MODE=prod + TZ="Europe/London" + dest: /opt/peekaping/.env + mode: '0600' + + - name: Start Peekaping services + shell: | + cd /opt/peekaping + docker compose up -d + args: + executable: /bin/bash + + - name: Wait for Peekaping API to start + uri: + url: "http://localhost:{{ docker_port }}/api/v1/health" + method: GET + status_code: 200 + register: peekaping_health + retries: 30 + delay: 10 + until: peekaping_health.status == 200 + + - name: Run Certbot to get HTTPS certificate + include_role: + name: certbot + when: enable_https | default(true) | bool + + - name: Deploy SSL Nginx configuration after certificates are obtained + include_role: + name: nginx + vars: + skip_install: true + when: enable_https | default(true) | bool + + - name: Display success message + debug: + msg: | + Peekaping has been successfully deployed! + + Service Details: + ---------------- + Domain: {{ domain }} + External Port: {{ docker_port }} + Data Location: {{ data_mount_point }} + + Services Running: + - Redis (message broker) + - API (REST API server) + - Producer (job scheduler) + - Worker (monitoring executor) + - Ingester (result processor) + - Web (frontend SPA) + - Gateway (Nginx reverse proxy) + + Access your Peekaping instance at: + https://{{ domain }} + + Note: Ensure DNS A record for {{ domain }} points to this server's IP. diff --git a/docker/peekaping/.env.example b/docker/peekaping/.env.example new file mode 100644 index 0000000..8f199f7 --- /dev/null +++ b/docker/peekaping/.env.example @@ -0,0 +1,24 @@ +# Database Configuration +DB_USER=root +DB_PASS=your-secure-password-here +DB_NAME=/app/data/peekaping.db +DB_TYPE=sqlite + +# Server Configuration +SERVER_PORT=8034 +CLIENT_URL="http://localhost:8383" + +# Application Settings +MODE=prod +TZ="Europe/London" + +# Data Path (for production VM) +# Local dev: ./.data/sqlite (default in docker-compose) +# Production VM: /mnt/peekaping (set in Ansible) +DATA_PATH=./.data/sqlite + +# JWT settings are automatically managed in the database +# Default settings are initialized on first startup: +# - Access token expiration: 15 minutes +# - Refresh token expiration: 720 hours (30 days) +# - Secret keys are automatically generated securely diff --git a/docker/peekaping/README.md b/docker/peekaping/README.md new file mode 100644 index 0000000..b0147e2 --- /dev/null +++ b/docker/peekaping/README.md @@ -0,0 +1,81 @@ +# Peekaping Local Development + +This directory contains Docker Compose configuration for running Peekaping locally. + +## Quick Start + +```bash +# Copy environment variables +cp .env.example .env + +# Start all services +docker compose up -d + +# Check status +docker compose ps + +# View logs +docker compose logs -f + +# Stop services +docker compose down +``` + +## Access + +Once running, Peekaping will be available at: +- **Web Interface**: http://localhost:8383 +- **API**: http://localhost:8383/api/ + +## Services + +- **redis**: Message broker for microservices +- **migrate**: Database migrations (runs once) +- **api**: REST API server (port 8034 internal) +- **producer**: Job scheduler for monitoring tasks +- **worker**: Executes monitoring checks +- **ingester**: Processes monitoring results +- **web**: Frontend SPA +- **gateway**: Nginx reverse proxy (port 8383 external) + +## Data Persistence + +Data is stored in `./.data/sqlite` directory. To reset: + +```bash +docker compose down -v +rm -rf .data +docker compose up -d +``` + +## Configuration + +Edit `.env` file to customize: +- Database settings +- Timezone +- Client URL +- Server port + +## Troubleshooting + +### Check service health +```bash +docker compose ps +``` + +### View specific service logs +```bash +docker compose logs -f api +docker compose logs -f worker +``` + +### Restart a service +```bash +docker compose restart api +``` + +### Clean restart +```bash +docker compose down +docker compose up -d +``` diff --git a/docker/peekaping/docker-compose.yml b/docker/peekaping/docker-compose.yml new file mode 100644 index 0000000..3d86c31 --- /dev/null +++ b/docker/peekaping/docker-compose.yml @@ -0,0 +1,126 @@ +version: '3.8' + +networks: + appnet: + driver: bridge + +services: + redis: + image: redis:7 + restart: unless-stopped + networks: + - appnet + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 30s + timeout: 2s + retries: 5 + start_period: 5s + + migrate: + image: 0xfurai/peekaping-migrate:latest + restart: "no" + env_file: + - .env + volumes: + - ${DATA_PATH:-./.data/sqlite}:/app/data + + api: + image: 0xfurai/peekaping-api:latest + restart: unless-stopped + env_file: + - .env + volumes: + - ${DATA_PATH:-./.data/sqlite}:/app/data + depends_on: + redis: + condition: service_started + migrate: + condition: service_completed_successfully + networks: + - appnet + healthcheck: + test: + [ + "CMD-SHELL", + "wget -qO - http://localhost:8034/api/v1/health || exit 1", + ] + interval: 30s + timeout: 2s + retries: 5 + start_period: 5s + + producer: + image: 0xfurai/peekaping-producer:latest + restart: unless-stopped + env_file: + - .env + volumes: + - ${DATA_PATH:-./.data/sqlite}:/app/data + depends_on: + redis: + condition: service_healthy + migrate: + condition: service_completed_successfully + networks: + - appnet + + worker: + image: 0xfurai/peekaping-worker:latest + restart: unless-stopped + env_file: + - .env + depends_on: + redis: + condition: service_healthy + networks: + - appnet + + ingester: + image: 0xfurai/peekaping-ingester:latest + restart: unless-stopped + env_file: + - .env + volumes: + - ${DATA_PATH:-./.data/sqlite}:/app/data + depends_on: + redis: + condition: service_started + migrate: + condition: service_completed_successfully + networks: + - appnet + + web: + image: 0xfurai/peekaping-web:latest + depends_on: + api: + condition: service_healthy + networks: + - appnet + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"] + interval: 30s + timeout: 2s + retries: 5 + start_period: 5s + + gateway: + image: nginx:latest + ports: + - "8383:80" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + depends_on: + api: + condition: service_healthy + web: + condition: service_healthy + networks: + - appnet + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"] + interval: 30s + timeout: 2s + retries: 5 + start_period: 5s diff --git a/docker/peekaping/nginx.conf b/docker/peekaping/nginx.conf new file mode 100644 index 0000000..e57a3ee --- /dev/null +++ b/docker/peekaping/nginx.conf @@ -0,0 +1,30 @@ +events {} +http { + upstream api { server api:8034; } + upstream web { server web:80; } + + server { + listen 80; + + # Pure API calls + location /api/ { + proxy_pass http://api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # socket.io + location /socket.io/ { + proxy_pass http://api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + # Everything else → static SPA + location / { + proxy_pass http://web; + } + } +} diff --git a/terraform/base/main.tf b/terraform/base/main.tf index 4866943..23fefd1 100644 --- a/terraform/base/main.tf +++ b/terraform/base/main.tf @@ -8,3 +8,11 @@ module "uptime_kuma" { admin_email = var.admin_email environment = var.environment } + +module "peekaping" { + source = "../services/peekaping" + + hetzner_token = var.hetzner_token + admin_email = var.admin_email + environment = var.environment +} diff --git a/terraform/services/peekaping/README.md b/terraform/services/peekaping/README.md new file mode 100644 index 0000000..abad2ea --- /dev/null +++ b/terraform/services/peekaping/README.md @@ -0,0 +1,280 @@ +# Peekaping Service + +This Terraform module provisions infrastructure for Peekaping, a modern open-source uptime monitoring and status page service. + +## Overview + +Peekaping is deployed in microservice mode with the following components: +- **Redis**: Message broker for inter-service communication +- **API**: REST API server for backend operations +- **Producer**: Job scheduler for monitoring tasks +- **Worker**: Executes monitoring checks +- **Ingester**: Processes and stores monitoring results +- **Web**: Frontend SPA (Single Page Application) +- **Gateway**: Nginx reverse proxy for routing + +## Infrastructure + +### Hetzner Resources + +- **Server**: CX23 (2 vCPU, 4GB RAM, ~€5.83/month) +- **Location**: Nuremberg, Germany (nbg1) +- **Volume**: 10GB persistent storage for SQLite database +- **OS**: Ubuntu with Docker + +### Components + +1. **Hetzner VM**: Provisioned using WebKit's server module +2. **Persistent Volume**: 10GB ext4 volume for database storage +3. **Ansible Inventory**: Auto-generated for deployment + +## Configuration + +### Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `hetzner_token` | Hetzner Cloud API token | (required) | +| `service_name` | Name of the service | `peekaping` | +| `server_type` | Hetzner server type | `cx23` | +| `location` | Hetzner datacenter location | `nbg1` | +| `volume_size` | Data volume size in GB | `10` | +| `domain` | Domain for the instance | `peekaping.ainsley.dev` | +| `admin_email` | Admin email for SSL certs | `hello@ainsley.dev` | +| `environment` | Environment name | `production` | +| `tags` | Resource tags | `["peekaping", "monitoring"]` | + +### Customization + +To customize the deployment, create a `terraform.tfvars` file: + +```hcl +hetzner_token = "your-token-here" +domain = "monitoring.example.com" +admin_email = "admin@example.com" +server_type = "cx32" # Larger server if needed +volume_size = 20 # More storage if needed +``` + +## Deployment Workflow + +### 1. Initialize Terraform + +```bash +make init +``` + +This initializes Terraform with the Backblaze B2 backend. + +### 2. Plan Infrastructure Changes + +```bash +make plan +``` + +Review the planned changes before applying. + +### 3. Apply Infrastructure + +```bash +make apply +``` + +This will: +- Create the Hetzner VM +- Attach the persistent volume +- Generate Ansible inventory file + +### 4. Deploy Peekaping + +```bash +make deploy-peekaping +``` + +This runs the Ansible playbook which: +- Installs Docker and dependencies +- Configures firewall (UFW) +- Sets up fail2ban +- Mounts the Hetzner volume +- Deploys Peekaping via Docker Compose +- Configures Nginx reverse proxy +- Obtains SSL certificate (Let's Encrypt) + +### 5. Access Peekaping + +Once deployed, access your instance at: +``` +https://peekaping.ainsley.dev +``` + +## Outputs + +| Output | Description | +|--------|-------------| +| `server_id` | Hetzner server ID | +| `ip_address` | Public IP address | +| `ssh_private_key` | SSH private key (sensitive) | +| `ssh_public_key` | SSH public key | +| `volume_id` | Hetzner volume ID | +| `domain` | Configured domain | +| `server_user` | SSH username | + +### Export SSH Key + +To SSH into the server: + +```bash +make ssh-key-peekaping +ssh -i peekaping-key.pem root@ +``` + +## Maintenance + +### Update Peekaping + +```bash +# SSH into the server +ssh -i peekaping-key.pem root@ + +# Pull latest images +cd /opt/peekaping +docker compose pull + +# Restart with new images +docker compose up -d + +# Clean up old images +docker image prune -f +``` + +### View Logs + +```bash +# All services +docker compose logs -f + +# Specific service +docker compose logs -f api +docker compose logs -f worker +``` + +### Check Service Status + +```bash +docker compose ps +``` + +### Backup Database + +The SQLite database is stored on the Hetzner volume at `/mnt/peekaping/peekaping.db`. + +To backup: + +```bash +# SSH into server +ssh -i peekaping-key.pem root@ + +# Create backup +cp /mnt/peekaping/peekaping.db /mnt/peekaping/peekaping.db.backup-$(date +%Y%m%d) + +# Or download locally +scp -i peekaping-key.pem root@:/mnt/peekaping/peekaping.db ./peekaping-backup.db +``` + +## Troubleshooting + +### Services Not Starting + +Check logs for specific services: + +```bash +docker compose logs api +docker compose logs worker +``` + +### Health Check Failed + +Verify API is responding: + +```bash +curl http://localhost:8383/api/v1/health +``` + +### SSL Certificate Issues + +Re-run Certbot: + +```bash +certbot --nginx -d peekaping.ainsley.dev +``` + +### Volume Not Mounted + +Check if volume is attached: + +```bash +lsblk +mount | grep peekaping +``` + +## Architecture Diagram + +``` + Internet + | + [HTTPS] + | + +----------v----------+ + | Nginx (Host) | + | SSL/TLS | + +----------+----------+ + | + +----------v----------+ + | Gateway Container | + | (Nginx) | + +----------+----------+ + | + +------------------+------------------+ + | | + +-------v--------+ +-------v--------+ + | API | | Web | + | (Port 8034) | | (SPA) | + +-------+--------+ +----------------+ + | + +-----------+------------+ + | | | ++-------v---+ +-----v-----+ +----v------+ +| Producer | | Ingester | | Worker | ++-----------+ +-----------+ +-----------+ + | | | + +-----------|------------+ + | + +-------v--------+ + | Redis | + +----------------+ + | + +-------v--------+ + | SQLite DB | + | /mnt/peekaping | + +----------------+ +``` + +## Cost Estimation + +- **Server (CX23)**: ~€5.83/month +- **Volume (10GB)**: ~€0.50/month +- **Total**: ~€6.33/month + +## Security + +- Firewall (UFW) configured to allow only necessary ports +- fail2ban installed for intrusion prevention +- SSL/TLS encryption via Let's Encrypt +- SSH key-based authentication +- Automated security updates + +## References + +- [Peekaping Documentation](https://docs.peekaping.com/) +- [Hetzner Cloud](https://www.hetzner.com/cloud) +- [WebKit Infrastructure](https://github.com/ainsleydev/webkit) diff --git a/terraform/services/peekaping/main.tf b/terraform/services/peekaping/main.tf new file mode 100644 index 0000000..92c2362 --- /dev/null +++ b/terraform/services/peekaping/main.tf @@ -0,0 +1,70 @@ +# Peekaping Service Module +# +# This module creates a Hetzner VM for Peekaping monitoring +# service using WebKit's server/volume modules. + +terraform { + required_providers { + hcloud = { + source = "hetznercloud/hcloud" + version = "~> 1.0" + } + local = { + source = "hashicorp/local" + version = "~> 2.0" + } + } +} + +provider "hcloud" { + token = var.hetzner_token +} + +# Hetzner Server (WebKit) +# +# This creates the VM, SSH keys, firewall, and installs Ansible via cloud-init. +module "server" { + source = "github.com/ainsleydev/webkit//platform/terraform/providers/hetzner/server?ref=main" + + name = var.service_name + server_type = var.server_type + location = var.location + tags = var.tags + ssh_key_ids = ["hello@ainsley.dev", "ainsley.clark@GGYN90GJW7"] +} + +# Hetzner Volume (Webkit) +# +# Uses persistent data to save the Peekaping SQLite database. +# Note: lifecycle blocks cannot be used on modules, only resources. +# Data protection is provided by state backups and approval gates in CI/CD. +module "volume" { + source = "github.com/ainsleydev/webkit//platform/terraform/providers/hetzner/volume?ref=main" + + name = "${var.service_name}-data" + size = var.volume_size + location = var.location + server_id = module.server.id + format = "ext4" + automount = true + tags = concat(var.tags, [var.environment]) +} + +# Ansible Config +# +# Generate Ansible inventory file automatically +resource "local_file" "ansible_inventory" { + filename = "${path.root}/../../ansible/inventory-peekaping.ini" + content = <<-EOT + # Auto-generated by Terraform - DO NOT EDIT MANUALLY + + [peekaping] + ${var.domain} ansible_host=${module.server.ip_address} ansible_user=${module.server.server_user} + + [peekaping:vars] + domain=${var.domain} + admin_email=${var.admin_email} + EOT + + file_permission = "0644" +} diff --git a/terraform/services/peekaping/outputs.tf b/terraform/services/peekaping/outputs.tf new file mode 100644 index 0000000..c87653c --- /dev/null +++ b/terraform/services/peekaping/outputs.tf @@ -0,0 +1,35 @@ +output "server_id" { + description = "The ID of the Hetzner server" + value = module.server.id +} + +output "ip_address" { + description = "The public IP address of the server" + value = module.server.ip_address +} + +output "ssh_private_key" { + description = "SSH private key for server access" + value = module.server.ssh_private_key + sensitive = true +} + +output "ssh_public_key" { + description = "SSH public key for server access" + value = module.server.ssh_public_key +} + +output "volume_id" { + description = "The ID of the Hetzner volume" + value = module.volume.id +} + +output "domain" { + description = "The domain for the Peekaping instance" + value = var.domain +} + +output "server_user" { + description = "SSH user for the server" + value = module.server.server_user +} diff --git a/terraform/services/peekaping/variables.tf b/terraform/services/peekaping/variables.tf new file mode 100644 index 0000000..63ec0a9 --- /dev/null +++ b/terraform/services/peekaping/variables.tf @@ -0,0 +1,53 @@ +variable "hetzner_token" { + type = string + description = "Hetzner Cloud API token" + sensitive = true +} + +variable "service_name" { + type = string + description = "Name of the service" + default = "peekaping" +} + +variable "server_type" { + type = string + description = "Hetzner server type (CX23 = 2 vCPU, 4GB RAM, ~€5.83/month)" + default = "cx23" +} + +variable "location" { + type = string + description = "Hetzner datacenter location" + default = "nbg1" # Nuremberg, Germany +} + +variable "volume_size" { + type = number + description = "Size of the data volume in GB" + default = 10 +} + +variable "domain" { + type = string + description = "Domain for the Peekaping instance" + default = "peekaping.ainsley.dev" +} + +variable "admin_email" { + type = string + description = "Admin email for Let's Encrypt certificates" + default = "hello@ainsley.dev" +} + +variable "environment" { + type = string + description = "Environment name" + default = "production" +} + +variable "tags" { + type = list(string) + description = "Tags to apply to resources" + default = ["peekaping", "monitoring"] +} From 344daed3d906b286e919fb203fa46975537726f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Nov 2025 21:06:17 +0000 Subject: [PATCH 02/12] refactor: Copy docker-compose and nginx config files instead of duplicating inline Replace inline content duplication with proper file copying from docker/peekaping/ directory. This follows DRY principles and makes the configuration easier to maintain by keeping a single source of truth for these files. Changes: - Copy docker-compose.yml from docker/peekaping/ instead of inline definition - Copy nginx.conf from docker/peekaping/ instead of inline definition - Add replace task to substitute DATA_PATH for production mount point --- ansible/playbooks/peekaping.yaml | 166 ++----------------------------- 1 file changed, 8 insertions(+), 158 deletions(-) diff --git a/ansible/playbooks/peekaping.yaml b/ansible/playbooks/peekaping.yaml index 41cfe04..d32303f 100644 --- a/ansible/playbooks/peekaping.yaml +++ b/ansible/playbooks/peekaping.yaml @@ -96,169 +96,19 @@ - name: Copy docker-compose.yml to server copy: - content: | - version: '3.8' - - networks: - appnet: - driver: bridge - - services: - redis: - image: redis:7 - restart: unless-stopped - networks: - - appnet - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 30s - timeout: 2s - retries: 5 - start_period: 5s - - migrate: - image: 0xfurai/peekaping-migrate:latest - restart: "no" - env_file: - - .env - volumes: - - {{ data_mount_point }}:/app/data - - api: - image: 0xfurai/peekaping-api:latest - restart: unless-stopped - env_file: - - .env - volumes: - - {{ data_mount_point }}:/app/data - depends_on: - redis: - condition: service_started - migrate: - condition: service_completed_successfully - networks: - - appnet - healthcheck: - test: - [ - "CMD-SHELL", - "wget -qO - http://localhost:8034/api/v1/health || exit 1", - ] - interval: 30s - timeout: 2s - retries: 5 - start_period: 5s - - producer: - image: 0xfurai/peekaping-producer:latest - restart: unless-stopped - env_file: - - .env - volumes: - - {{ data_mount_point }}:/app/data - depends_on: - redis: - condition: service_healthy - migrate: - condition: service_completed_successfully - networks: - - appnet - - worker: - image: 0xfurai/peekaping-worker:latest - restart: unless-stopped - env_file: - - .env - depends_on: - redis: - condition: service_healthy - networks: - - appnet - - ingester: - image: 0xfurai/peekaping-ingester:latest - restart: unless-stopped - env_file: - - .env - volumes: - - {{ data_mount_point }}:/app/data - depends_on: - redis: - condition: service_started - migrate: - condition: service_completed_successfully - networks: - - appnet - - web: - image: 0xfurai/peekaping-web:latest - depends_on: - api: - condition: service_healthy - networks: - - appnet - healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"] - interval: 30s - timeout: 2s - retries: 5 - start_period: 5s - - gateway: - image: nginx:latest - ports: - - "{{ docker_port }}:80" - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf:ro - depends_on: - api: - condition: service_healthy - web: - condition: service_healthy - networks: - - appnet - healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:80 || exit 1"] - interval: 30s - timeout: 2s - retries: 5 - start_period: 5s + src: "{{ playbook_dir }}/../docker/peekaping/docker-compose.yml" dest: /opt/peekaping/docker-compose.yml mode: '0644' + - name: Replace DATA_PATH variable in docker-compose.yml + replace: + path: /opt/peekaping/docker-compose.yml + regexp: '\$\{DATA_PATH:-\.\/.data\/sqlite\}' + replace: '{{ data_mount_point }}' + - name: Copy nginx.conf to server copy: - content: | - events {} - http { - upstream api { server api:8034; } - upstream web { server web:80; } - - server { - listen 80; - - # Pure API calls - location /api/ { - proxy_pass http://api; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - } - - # socket.io - location /socket.io/ { - proxy_pass http://api; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } - - # Everything else → static SPA - location / { - proxy_pass http://web; - } - } - } + src: "{{ playbook_dir }}/../docker/peekaping/nginx.conf" dest: /opt/peekaping/nginx.conf mode: '0644' From f6a5acd0cf8385e9256ef430dec64631fbe27faf Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Nov 2025 21:10:06 +0000 Subject: [PATCH 03/12] feat: Add version pinning support for Peekaping deployments Implement comprehensive version management to enable reproducible deployments and controlled upgrades. This addresses production stability concerns by allowing specific version pinning instead of always using 'latest' tags. Changes: - Add PEEKAPING_VERSION environment variable with default 'latest' - Update docker-compose.yml to use version variable for all services - Add peekaping_version variable to Ansible playbook - Create comprehensive VERSIONING.md guide covering: - Version pinning best practices - Upgrade procedures (local and production) - Rollback instructions - Monitoring current versions - Update documentation with version management guidance Usage: - Development: PEEKAPING_VERSION=latest - Production: PEEKAPING_VERSION=v1.2.3 Check releases: https://github.com/0xfurai/peekaping/releases --- VERSIONING.md | 223 +++++++++++++++++++++++++ ansible/playbooks/peekaping.yaml | 6 + docker/peekaping/.env.example | 6 + docker/peekaping/README.md | 17 ++ docker/peekaping/docker-compose.yml | 12 +- terraform/services/peekaping/README.md | 19 ++- 6 files changed, 276 insertions(+), 7 deletions(-) create mode 100644 VERSIONING.md diff --git a/VERSIONING.md b/VERSIONING.md new file mode 100644 index 0000000..2c597e9 --- /dev/null +++ b/VERSIONING.md @@ -0,0 +1,223 @@ +# Peekaping Version Management + +This document describes how to manage Peekaping versions for reproducible deployments. + +## Current Version + +The version is controlled by the `PEEKAPING_VERSION` environment variable, which defaults to `latest` but should be pinned to specific versions in production. + +## Checking for New Versions + +Visit the Peekaping releases page to see available versions: +``` +https://github.com/0xfurai/peekaping/releases +``` + +## Version Pinning + +### Local Development + +Edit your `.env` file in `docker/peekaping/`: + +```bash +# Use latest for development +PEEKAPING_VERSION=latest + +# Or pin to specific version +PEEKAPING_VERSION=v1.2.3 +``` + +### Production Deployment + +The production version is controlled in the Ansible playbook vars at `ansible/playbooks/peekaping.yaml`: + +```yaml +vars: + peekaping_version: v1.2.3 # Pin to specific version +``` + +You can also override this during deployment: + +```bash +cd ansible +ansible-playbook -i inventory-peekaping.ini playbooks/peekaping.yaml \ + -e "peekaping_version=v1.2.3" +``` + +## Upgrading Peekaping + +### Pre-Upgrade Checklist + +1. ✅ Backup your database: + ```bash + ssh -i peekaping-key.pem root@ + cp /mnt/peekaping/peekaping.db /mnt/peekaping/backup-$(date +%Y%m%d).db + ``` + +2. ✅ Check the release notes for breaking changes: + ``` + https://github.com/0xfurai/peekaping/releases + ``` + +3. ✅ Test the new version locally first + +### Local Testing + +```bash +cd docker/peekaping + +# Update version in .env +echo "PEEKAPING_VERSION=v1.2.3" >> .env + +# Pull new images +docker compose pull + +# Restart services +docker compose up -d + +# Check logs +docker compose logs -f + +# Verify health +curl http://localhost:8383/api/v1/health +``` + +### Production Upgrade + +#### Method 1: Update Playbook (Recommended for version tracking) + +1. Edit `ansible/playbooks/peekaping.yaml`: + ```yaml + vars: + peekaping_version: v1.2.3 # Update to new version + ``` + +2. Commit the change: + ```bash + git add ansible/playbooks/peekaping.yaml + git commit -m "chore: Upgrade Peekaping to v1.2.3" + git push + ``` + +3. Re-run the deployment: + ```bash + make deploy-peekaping + ``` + +#### Method 2: Override at Runtime (Quick updates) + +```bash +cd ansible +ansible-playbook -i inventory-peekaping.ini playbooks/peekaping.yaml \ + -e "peekaping_version=v1.2.3" \ + --tags upgrade +``` + +#### Method 3: Manual SSH Update (Emergency only) + +```bash +# SSH into server +ssh -i peekaping-key.pem root@ + +# Navigate to deployment directory +cd /opt/peekaping + +# Update version in .env +sed -i 's/PEEKAPING_VERSION=.*/PEEKAPING_VERSION=v1.2.3/' .env + +# Pull new images +docker compose pull + +# Restart services with zero downtime +docker compose up -d + +# Monitor logs +docker compose logs -f api worker producer +``` + +## Version Rollback + +If an upgrade causes issues, you can rollback: + +```bash +# SSH into server +ssh -i peekaping-key.pem root@ + +cd /opt/peekaping + +# Rollback to previous version +sed -i 's/PEEKAPING_VERSION=.*/PEEKAPING_VERSION=v1.2.2/' .env + +# Pull old images (if still available) +docker compose pull + +# Restart +docker compose up -d +``` + +## Best Practices + +### Production Deployments + +✅ **DO**: +- Pin to specific versions (e.g., `v1.2.3`) +- Track version changes in git +- Test upgrades in development first +- Backup database before upgrades +- Read release notes for breaking changes +- Monitor logs after upgrades + +❌ **DON'T**: +- Use `latest` in production +- Auto-update without testing +- Skip backups before upgrades +- Ignore deprecation warnings + +### Development + +✅ **DO**: +- Use `latest` to stay current +- Test new versions before production +- Document any issues found + +## Monitoring Current Version + +Check what version is currently running: + +```bash +# Local +cd docker/peekaping +grep PEEKAPING_VERSION .env + +# Production (via SSH) +ssh -i peekaping-key.pem root@ "grep PEEKAPING_VERSION /opt/peekaping/.env" + +# Check running container images +docker compose ps --format "table {{.Service}}\t{{.Image}}" +``` + +## Version History + +Keep track of version changes in your git history: + +```bash +git log --all --grep="Peekaping" --oneline +``` + +## Automation (Future Enhancement) + +Consider adding a Makefile target for easier version management: + +```makefile +upgrade-peekaping: # Upgrade Peekaping to specified version + @read -p "Enter version (e.g., v1.2.3): " version; \ + sed -i "s/peekaping_version: .*/peekaping_version: $$version/" ansible/playbooks/peekaping.yaml && \ + echo "Updated to version $$version" && \ + echo "Run 'make deploy-peekaping' to apply the upgrade" +``` + +## Support + +- **Releases**: https://github.com/0xfurai/peekaping/releases +- **Documentation**: https://docs.peekaping.com/ +- **Docker Hub**: https://hub.docker.com/u/0xfurai diff --git a/ansible/playbooks/peekaping.yaml b/ansible/playbooks/peekaping.yaml index d32303f..61e3907 100644 --- a/ansible/playbooks/peekaping.yaml +++ b/ansible/playbooks/peekaping.yaml @@ -10,6 +10,9 @@ service_name: peekaping data_mount_point: /mnt/peekaping enable_https: true + # Pin to specific version for production stability + # Check releases: https://github.com/0xfurai/peekaping/releases + peekaping_version: latest pre_tasks: - name: Update apt package cache @@ -115,6 +118,9 @@ - name: Create .env file for Peekaping copy: content: | + # Peekaping Version + PEEKAPING_VERSION={{ peekaping_version }} + # Database Configuration DB_USER=root DB_PASS={{ lookup('password', '/tmp/peekaping_db_pass chars=ascii_letters,digits length=32') }} diff --git a/docker/peekaping/.env.example b/docker/peekaping/.env.example index 8f199f7..3ae3a91 100644 --- a/docker/peekaping/.env.example +++ b/docker/peekaping/.env.example @@ -1,3 +1,9 @@ +# Peekaping Version +# Pin to a specific version for production stability, or use 'latest' for development +# Check releases: https://github.com/0xfurai/peekaping/releases +# Example: PEEKAPING_VERSION=v1.2.3 +PEEKAPING_VERSION=latest + # Database Configuration DB_USER=root DB_PASS=your-secure-password-here diff --git a/docker/peekaping/README.md b/docker/peekaping/README.md index b0147e2..0798404 100644 --- a/docker/peekaping/README.md +++ b/docker/peekaping/README.md @@ -51,11 +51,28 @@ docker compose up -d ## Configuration Edit `.env` file to customize: +- **Version pinning** (`PEEKAPING_VERSION`) - Pin to specific version for stability - Database settings - Timezone - Client URL - Server port +### Version Management + +**For production**, pin to specific versions: +```bash +PEEKAPING_VERSION=v1.2.3 +``` + +**For development**, use latest: +```bash +PEEKAPING_VERSION=latest +``` + +Check available versions at: https://github.com/0xfurai/peekaping/releases + +See [VERSIONING.md](../../../VERSIONING.md) for upgrade procedures. + ## Troubleshooting ### Check service health diff --git a/docker/peekaping/docker-compose.yml b/docker/peekaping/docker-compose.yml index 3d86c31..202af4f 100644 --- a/docker/peekaping/docker-compose.yml +++ b/docker/peekaping/docker-compose.yml @@ -18,7 +18,7 @@ services: start_period: 5s migrate: - image: 0xfurai/peekaping-migrate:latest + image: 0xfurai/peekaping-migrate:${PEEKAPING_VERSION:-latest} restart: "no" env_file: - .env @@ -26,7 +26,7 @@ services: - ${DATA_PATH:-./.data/sqlite}:/app/data api: - image: 0xfurai/peekaping-api:latest + image: 0xfurai/peekaping-api:${PEEKAPING_VERSION:-latest} restart: unless-stopped env_file: - .env @@ -51,7 +51,7 @@ services: start_period: 5s producer: - image: 0xfurai/peekaping-producer:latest + image: 0xfurai/peekaping-producer:${PEEKAPING_VERSION:-latest} restart: unless-stopped env_file: - .env @@ -66,7 +66,7 @@ services: - appnet worker: - image: 0xfurai/peekaping-worker:latest + image: 0xfurai/peekaping-worker:${PEEKAPING_VERSION:-latest} restart: unless-stopped env_file: - .env @@ -77,7 +77,7 @@ services: - appnet ingester: - image: 0xfurai/peekaping-ingester:latest + image: 0xfurai/peekaping-ingester:${PEEKAPING_VERSION:-latest} restart: unless-stopped env_file: - .env @@ -92,7 +92,7 @@ services: - appnet web: - image: 0xfurai/peekaping-web:latest + image: 0xfurai/peekaping-web:${PEEKAPING_VERSION:-latest} depends_on: api: condition: service_healthy diff --git a/terraform/services/peekaping/README.md b/terraform/services/peekaping/README.md index abad2ea..6906ce7 100644 --- a/terraform/services/peekaping/README.md +++ b/terraform/services/peekaping/README.md @@ -132,12 +132,29 @@ ssh -i peekaping-key.pem root@ ### Update Peekaping +**Important**: See [VERSIONING.md](../../../VERSIONING.md) for detailed upgrade procedures. + +Quick upgrade: + +```bash +# 1. Update version in ansible/playbooks/peekaping.yaml +peekaping_version: v1.2.3 + +# 2. Re-run deployment +make deploy-peekaping +``` + +Or manually via SSH: + ```bash # SSH into the server ssh -i peekaping-key.pem root@ -# Pull latest images +# Update version in .env cd /opt/peekaping +sed -i 's/PEEKAPING_VERSION=.*/PEEKAPING_VERSION=v1.2.3/' .env + +# Pull new images docker compose pull # Restart with new images From 5a72b36c1f3332a4a6ebf09a2bb059028909f666 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Nov 2025 07:08:25 +0000 Subject: [PATCH 04/12] docs: Consolidate version management docs into READMEs Remove separate VERSIONING.md file and consolidate all version management documentation directly into the relevant README files for easier reference. Changes: - Delete VERSIONING.md - Add condensed version management section to terraform/services/peekaping/README.md - Add condensed version management section to docker/peekaping/README.md - Keep essential info: version pinning, upgrade procedures, best practices - Remove redundancy while maintaining all critical information All version management documentation is now in context where it's needed. --- VERSIONING.md | 223 ------------------------- docker/peekaping/README.md | 17 +- terraform/services/peekaping/README.md | 35 ++-- 3 files changed, 35 insertions(+), 240 deletions(-) delete mode 100644 VERSIONING.md diff --git a/VERSIONING.md b/VERSIONING.md deleted file mode 100644 index 2c597e9..0000000 --- a/VERSIONING.md +++ /dev/null @@ -1,223 +0,0 @@ -# Peekaping Version Management - -This document describes how to manage Peekaping versions for reproducible deployments. - -## Current Version - -The version is controlled by the `PEEKAPING_VERSION` environment variable, which defaults to `latest` but should be pinned to specific versions in production. - -## Checking for New Versions - -Visit the Peekaping releases page to see available versions: -``` -https://github.com/0xfurai/peekaping/releases -``` - -## Version Pinning - -### Local Development - -Edit your `.env` file in `docker/peekaping/`: - -```bash -# Use latest for development -PEEKAPING_VERSION=latest - -# Or pin to specific version -PEEKAPING_VERSION=v1.2.3 -``` - -### Production Deployment - -The production version is controlled in the Ansible playbook vars at `ansible/playbooks/peekaping.yaml`: - -```yaml -vars: - peekaping_version: v1.2.3 # Pin to specific version -``` - -You can also override this during deployment: - -```bash -cd ansible -ansible-playbook -i inventory-peekaping.ini playbooks/peekaping.yaml \ - -e "peekaping_version=v1.2.3" -``` - -## Upgrading Peekaping - -### Pre-Upgrade Checklist - -1. ✅ Backup your database: - ```bash - ssh -i peekaping-key.pem root@ - cp /mnt/peekaping/peekaping.db /mnt/peekaping/backup-$(date +%Y%m%d).db - ``` - -2. ✅ Check the release notes for breaking changes: - ``` - https://github.com/0xfurai/peekaping/releases - ``` - -3. ✅ Test the new version locally first - -### Local Testing - -```bash -cd docker/peekaping - -# Update version in .env -echo "PEEKAPING_VERSION=v1.2.3" >> .env - -# Pull new images -docker compose pull - -# Restart services -docker compose up -d - -# Check logs -docker compose logs -f - -# Verify health -curl http://localhost:8383/api/v1/health -``` - -### Production Upgrade - -#### Method 1: Update Playbook (Recommended for version tracking) - -1. Edit `ansible/playbooks/peekaping.yaml`: - ```yaml - vars: - peekaping_version: v1.2.3 # Update to new version - ``` - -2. Commit the change: - ```bash - git add ansible/playbooks/peekaping.yaml - git commit -m "chore: Upgrade Peekaping to v1.2.3" - git push - ``` - -3. Re-run the deployment: - ```bash - make deploy-peekaping - ``` - -#### Method 2: Override at Runtime (Quick updates) - -```bash -cd ansible -ansible-playbook -i inventory-peekaping.ini playbooks/peekaping.yaml \ - -e "peekaping_version=v1.2.3" \ - --tags upgrade -``` - -#### Method 3: Manual SSH Update (Emergency only) - -```bash -# SSH into server -ssh -i peekaping-key.pem root@ - -# Navigate to deployment directory -cd /opt/peekaping - -# Update version in .env -sed -i 's/PEEKAPING_VERSION=.*/PEEKAPING_VERSION=v1.2.3/' .env - -# Pull new images -docker compose pull - -# Restart services with zero downtime -docker compose up -d - -# Monitor logs -docker compose logs -f api worker producer -``` - -## Version Rollback - -If an upgrade causes issues, you can rollback: - -```bash -# SSH into server -ssh -i peekaping-key.pem root@ - -cd /opt/peekaping - -# Rollback to previous version -sed -i 's/PEEKAPING_VERSION=.*/PEEKAPING_VERSION=v1.2.2/' .env - -# Pull old images (if still available) -docker compose pull - -# Restart -docker compose up -d -``` - -## Best Practices - -### Production Deployments - -✅ **DO**: -- Pin to specific versions (e.g., `v1.2.3`) -- Track version changes in git -- Test upgrades in development first -- Backup database before upgrades -- Read release notes for breaking changes -- Monitor logs after upgrades - -❌ **DON'T**: -- Use `latest` in production -- Auto-update without testing -- Skip backups before upgrades -- Ignore deprecation warnings - -### Development - -✅ **DO**: -- Use `latest` to stay current -- Test new versions before production -- Document any issues found - -## Monitoring Current Version - -Check what version is currently running: - -```bash -# Local -cd docker/peekaping -grep PEEKAPING_VERSION .env - -# Production (via SSH) -ssh -i peekaping-key.pem root@ "grep PEEKAPING_VERSION /opt/peekaping/.env" - -# Check running container images -docker compose ps --format "table {{.Service}}\t{{.Image}}" -``` - -## Version History - -Keep track of version changes in your git history: - -```bash -git log --all --grep="Peekaping" --oneline -``` - -## Automation (Future Enhancement) - -Consider adding a Makefile target for easier version management: - -```makefile -upgrade-peekaping: # Upgrade Peekaping to specified version - @read -p "Enter version (e.g., v1.2.3): " version; \ - sed -i "s/peekaping_version: .*/peekaping_version: $$version/" ansible/playbooks/peekaping.yaml && \ - echo "Updated to version $$version" && \ - echo "Run 'make deploy-peekaping' to apply the upgrade" -``` - -## Support - -- **Releases**: https://github.com/0xfurai/peekaping/releases -- **Documentation**: https://docs.peekaping.com/ -- **Docker Hub**: https://hub.docker.com/u/0xfurai diff --git a/docker/peekaping/README.md b/docker/peekaping/README.md index 0798404..4e8376a 100644 --- a/docker/peekaping/README.md +++ b/docker/peekaping/README.md @@ -59,19 +59,24 @@ Edit `.env` file to customize: ### Version Management -**For production**, pin to specific versions: ```bash +# Production (recommended) PEEKAPING_VERSION=v1.2.3 -``` -**For development**, use latest: -```bash +# Development PEEKAPING_VERSION=latest ``` -Check available versions at: https://github.com/0xfurai/peekaping/releases +**Upgrading**: +```bash +# Update version in .env +echo "PEEKAPING_VERSION=v1.2.3" >> .env + +# Pull new images and restart +docker compose pull && docker compose up -d +``` -See [VERSIONING.md](../../../VERSIONING.md) for upgrade procedures. +Check releases: https://github.com/0xfurai/peekaping/releases ## Troubleshooting diff --git a/terraform/services/peekaping/README.md b/terraform/services/peekaping/README.md index 6906ce7..db80b85 100644 --- a/terraform/services/peekaping/README.md +++ b/terraform/services/peekaping/README.md @@ -130,21 +130,35 @@ ssh -i peekaping-key.pem root@ ## Maintenance -### Update Peekaping +### Version Management -**Important**: See [VERSIONING.md](../../../VERSIONING.md) for detailed upgrade procedures. +Peekaping versions are controlled by the `PEEKAPING_VERSION` environment variable. -Quick upgrade: +**Check releases**: https://github.com/0xfurai/peekaping/releases + +**Best Practices**: +- ✅ Production: Pin to specific versions (e.g., `v1.2.3`) +- ✅ Development: Use `latest` to stay current +- ✅ Always backup database before upgrades +- ✅ Test new versions locally first + +### Upgrading Peekaping + +**Method 1: Via Ansible (Recommended)** ```bash -# 1. Update version in ansible/playbooks/peekaping.yaml +# 1. Backup database first +ssh -i peekaping-key.pem root@ +cp /mnt/peekaping/peekaping.db /mnt/peekaping/backup-$(date +%Y%m%d).db + +# 2. Update version in ansible/playbooks/peekaping.yaml peekaping_version: v1.2.3 -# 2. Re-run deployment +# 3. Re-run deployment make deploy-peekaping ``` -Or manually via SSH: +**Method 2: Manual SSH Update** ```bash # SSH into the server @@ -154,16 +168,15 @@ ssh -i peekaping-key.pem root@ cd /opt/peekaping sed -i 's/PEEKAPING_VERSION=.*/PEEKAPING_VERSION=v1.2.3/' .env -# Pull new images -docker compose pull - -# Restart with new images -docker compose up -d +# Pull and restart +docker compose pull && docker compose up -d # Clean up old images docker image prune -f ``` +**Rollback**: Change version back to previous in `.env` and restart + ### View Logs ```bash From 34fdf75af98944a5f1d0840ca62800c54c8864f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Nov 2025 07:15:19 +0000 Subject: [PATCH 05/12] feat: Add explicit domain configuration for monitoring services Make service domains explicitly configurable via terraform.tfvars instead of relying on module defaults. This makes it clear where to configure domains and ensures DNS setup is properly documented. Changes: - Add uptime_kuma_domain and peekaping_domain variables to terraform/base/variables.tf - Pass domain variables to service modules in terraform/base/main.tf - Add domain configuration section to terraform.tfvars.example with clear comments - Update peekaping README with explicit domain configuration instructions - Document DNS A record requirement Users now configure domains in terraform.tfvars: peekaping_domain = "monitoring.yourdomain.com" uptime_kuma_domain = "uptime.yourdomain.com" --- terraform.tfvars.example | 10 ++++++++++ terraform/base/main.tf | 2 ++ terraform/base/variables.tf | 13 +++++++++++++ terraform/services/peekaping/README.md | 16 +++++++++++----- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/terraform.tfvars.example b/terraform.tfvars.example index 898ebf7..7fe1ec2 100644 --- a/terraform.tfvars.example +++ b/terraform.tfvars.example @@ -21,3 +21,13 @@ environment = "production" # Project name (used for resource naming) project_name = "ainsley-dev-platform" + +# ===== Service Domains ===== +# Configure domains for each monitoring service +# Make sure DNS A records point to the server IPs after deployment + +# Uptime Kuma monitoring service +uptime_kuma_domain = "uptime.ainsley.dev" + +# Peekaping monitoring service +peekaping_domain = "peekaping.ainsley.dev" diff --git a/terraform/base/main.tf b/terraform/base/main.tf index 23fefd1..a962eca 100644 --- a/terraform/base/main.tf +++ b/terraform/base/main.tf @@ -5,6 +5,7 @@ module "uptime_kuma" { source = "../services/uptime-kuma" hetzner_token = var.hetzner_token + domain = var.uptime_kuma_domain admin_email = var.admin_email environment = var.environment } @@ -13,6 +14,7 @@ module "peekaping" { source = "../services/peekaping" hetzner_token = var.hetzner_token + domain = var.peekaping_domain admin_email = var.admin_email environment = var.environment } diff --git a/terraform/base/variables.tf b/terraform/base/variables.tf index a91a80a..9ddeabc 100644 --- a/terraform/base/variables.tf +++ b/terraform/base/variables.tf @@ -26,3 +26,16 @@ variable "admin_email" { description = "Admin email for Let's Encrypt certificates" default = "hello@ainsley.dev" } + +# Service-specific domains +variable "uptime_kuma_domain" { + type = string + description = "Domain for Uptime Kuma monitoring service" + default = "uptime.ainsley.dev" +} + +variable "peekaping_domain" { + type = string + description = "Domain for Peekaping monitoring service" + default = "peekaping.ainsley.dev" +} diff --git a/terraform/services/peekaping/README.md b/terraform/services/peekaping/README.md index db80b85..8454f5d 100644 --- a/terraform/services/peekaping/README.md +++ b/terraform/services/peekaping/README.md @@ -46,16 +46,22 @@ Peekaping is deployed in microservice mode with the following components: ### Customization -To customize the deployment, create a `terraform.tfvars` file: +Configure your domain and settings in `terraform.tfvars`: ```hcl +# Required hetzner_token = "your-token-here" -domain = "monitoring.example.com" -admin_email = "admin@example.com" -server_type = "cx32" # Larger server if needed -volume_size = 20 # More storage if needed + +# Service domains (customize these!) +peekaping_domain = "monitoring.yourdomain.com" + +# Optional overrides +admin_email = "admin@yourdomain.com" +environment = "production" ``` +**Important**: After deployment, create a DNS A record pointing your domain to the server IP. + ## Deployment Workflow ### 1. Initialize Terraform From 07a589b52fb46ae247d8f14672c805247a807082 Mon Sep 17 00:00:00 2001 From: Ainsley Clark Date: Thu, 20 Nov 2025 07:22:05 +0000 Subject: [PATCH 06/12] refactor: Updating ansible --- .gitignore | 1 - ansible/inventory-peekaping.ini | 8 ++++++++ terraform.tfvars.example | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 ansible/inventory-peekaping.ini diff --git a/.gitignore b/.gitignore index 0d6808e..269081d 100644 --- a/.gitignore +++ b/.gitignore @@ -136,7 +136,6 @@ repopack.txt **/*.retry **/ansible.log **/*.vault -**/inventory-*.ini # Environment files .envrc diff --git a/ansible/inventory-peekaping.ini b/ansible/inventory-peekaping.ini new file mode 100644 index 0000000..cef2e01 --- /dev/null +++ b/ansible/inventory-peekaping.ini @@ -0,0 +1,8 @@ +# Auto-generated by Terraform - DO NOT EDIT MANUALLY + +[peekaping] +uptime.ainsley.dev ansible_host=91.98.66.40 ansible_user=root + +[peekaping:vars] +domain=uptime.ainsley.dev +admin_email=hello@ainsley.dev diff --git a/terraform.tfvars.example b/terraform.tfvars.example index 7fe1ec2..3474c87 100644 --- a/terraform.tfvars.example +++ b/terraform.tfvars.example @@ -27,7 +27,7 @@ project_name = "ainsley-dev-platform" # Make sure DNS A records point to the server IPs after deployment # Uptime Kuma monitoring service -uptime_kuma_domain = "uptime.ainsley.dev" +uptime_kuma_domain = "old.ainsley.dev" # Peekaping monitoring service -peekaping_domain = "peekaping.ainsley.dev" +peekaping_domain = "uptime.ainsley.dev" From 4459897ebad42f069e12690ba1ca336d0cff894a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Nov 2025 07:26:28 +0000 Subject: [PATCH 07/12] fix: Correct file paths in Ansible playbook for docker config files The playbook is in ansible/playbooks/, so we need to go up two levels (../..) to reach the project root where docker/ directory is located. Fixes path from playbook_dir/../docker to playbook_dir/../../docker --- ansible/playbooks/peekaping.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ansible/playbooks/peekaping.yaml b/ansible/playbooks/peekaping.yaml index 61e3907..9176825 100644 --- a/ansible/playbooks/peekaping.yaml +++ b/ansible/playbooks/peekaping.yaml @@ -99,7 +99,7 @@ - name: Copy docker-compose.yml to server copy: - src: "{{ playbook_dir }}/../docker/peekaping/docker-compose.yml" + src: "{{ playbook_dir }}/../../docker/peekaping/docker-compose.yml" dest: /opt/peekaping/docker-compose.yml mode: '0644' @@ -111,7 +111,7 @@ - name: Copy nginx.conf to server copy: - src: "{{ playbook_dir }}/../docker/peekaping/nginx.conf" + src: "{{ playbook_dir }}/../../docker/peekaping/nginx.conf" dest: /opt/peekaping/nginx.conf mode: '0644' From aaebe7c2d76b581e562c8b325ca0c683ed475238 Mon Sep 17 00:00:00 2001 From: Ainsley Clark Date: Thu, 20 Nov 2025 07:33:12 +0000 Subject: [PATCH 08/12] refactor: Removing default domains from TF --- ansible/playbooks/peekaping.yaml | 2 +- terraform/base/variables.tf | 1 - terraform/services/peekaping/README.md | 4 +- terraform/services/peekaping/variables.tf | 1 - terraform/services/uptime-kuma/outputs.tf | 71 ----------------------- 5 files changed, 3 insertions(+), 76 deletions(-) diff --git a/ansible/playbooks/peekaping.yaml b/ansible/playbooks/peekaping.yaml index 9176825..c8d146c 100644 --- a/ansible/playbooks/peekaping.yaml +++ b/ansible/playbooks/peekaping.yaml @@ -4,7 +4,7 @@ become: true vars: # Variables to be passed via Terraform/cloud-init - # domain: peekaping.ainsley.dev + # domain: uptime.ainsley.dev # admin_email: hello@ainsley.dev docker_port: 8383 service_name: peekaping diff --git a/terraform/base/variables.tf b/terraform/base/variables.tf index 9ddeabc..201749c 100644 --- a/terraform/base/variables.tf +++ b/terraform/base/variables.tf @@ -37,5 +37,4 @@ variable "uptime_kuma_domain" { variable "peekaping_domain" { type = string description = "Domain for Peekaping monitoring service" - default = "peekaping.ainsley.dev" } diff --git a/terraform/services/peekaping/README.md b/terraform/services/peekaping/README.md index 8454f5d..be6d307 100644 --- a/terraform/services/peekaping/README.md +++ b/terraform/services/peekaping/README.md @@ -110,7 +110,7 @@ This runs the Ansible playbook which: Once deployed, access your instance at: ``` -https://peekaping.ainsley.dev +https://uptime.ainsley.dev ``` ## Outputs @@ -241,7 +241,7 @@ curl http://localhost:8383/api/v1/health Re-run Certbot: ```bash -certbot --nginx -d peekaping.ainsley.dev +certbot --nginx -d uptime.ainsley.dev ``` ### Volume Not Mounted diff --git a/terraform/services/peekaping/variables.tf b/terraform/services/peekaping/variables.tf index 63ec0a9..6c98a5d 100644 --- a/terraform/services/peekaping/variables.tf +++ b/terraform/services/peekaping/variables.tf @@ -31,7 +31,6 @@ variable "volume_size" { variable "domain" { type = string description = "Domain for the Peekaping instance" - default = "peekaping.ainsley.dev" } variable "admin_email" { diff --git a/terraform/services/uptime-kuma/outputs.tf b/terraform/services/uptime-kuma/outputs.tf index 5e88380..e69de29 100644 --- a/terraform/services/uptime-kuma/outputs.tf +++ b/terraform/services/uptime-kuma/outputs.tf @@ -1,71 +0,0 @@ -output "server_id" { - description = "Hetzner server ID" - value = module.server.id -} - -output "server_ip" { - description = "Public IPv4 address of the Uptime Kuma server" - value = module.server.ip_address -} - -output "ssh_private_key" { - description = "SSH private key for server access (generated by Terraform)" - value = module.server.ssh_private_key - sensitive = true -} - -output "ssh_public_key" { - description = "SSH public key" - value = module.server.ssh_public_key -} - -output "ssh_user" { - description = "SSH username for server access" - value = module.server.server_user -} - -output "volume_id" { - description = "Hetzner volume ID" - value = module.volume.id -} - -output "volume_device" { - description = "Volume device path on the server" - value = module.volume.linux_device -} - -output "domain" { - description = "Domain for Uptime Kuma instance" - value = var.domain -} - -output "ansible_inventory_path" { - description = "Path to the auto-generated Ansible inventory file" - value = local_file.ansible_inventory.filename -} - -output "access_instructions" { - description = "Instructions for accessing the server" - value = <<-EOT - Uptime Kuma Server Details: - --------------------------- - IP Address: ${module.server.ip_address} - Domain: ${var.domain} - SSH User: ${module.server.server_user} - - Ansible inventory file generated at: ${local_file.ansible_inventory.filename} - - To SSH into the server: - 1. Save the SSH private key: make ssh-key - 2. Connect: ssh -i uptime-kuma-key.pem ${module.server.server_user}@${module.server.ip_address} - - DNS Configuration Required: - --------------------------- - Add an A record for ${var.domain} pointing to ${module.server.ip_address} - - To deploy or update Uptime Kuma with Ansible: - make deploy-uptime - - Then access at: https://${var.domain} - EOT -} From b53501172c4fcae253b33fa3ef296e1c51e189f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Nov 2025 07:39:31 +0000 Subject: [PATCH 09/12] feat: Configure host Nginx to proxy to Peekaping Docker gateway Instead of exposing port 8383 directly to the internet, configure host Nginx to reverse proxy to the Docker gateway on localhost:8383. This is more secure and follows best practices. Changes: - Add Nginx site configuration for domain proxying to localhost:8383 - Enable WebSocket support for Socket.io connections - Create and enable Nginx site before running Certbot - Test and reload Nginx after configuration Security improvements: - Port 8383 remains internal-only - Only standard ports (22, 80, 443) exposed to internet - Nginx handles SSL/TLS termination - Application not directly accessible from internet --- ansible/playbooks/peekaping.yaml | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/ansible/playbooks/peekaping.yaml b/ansible/playbooks/peekaping.yaml index c8d146c..6f192ed 100644 --- a/ansible/playbooks/peekaping.yaml +++ b/ansible/playbooks/peekaping.yaml @@ -154,6 +154,53 @@ delay: 10 until: peekaping_health.status == 200 + - name: Configure Nginx to proxy to Peekaping + copy: + content: | + server { + listen 80; + listen [::]:80; + server_name {{ domain }}; + + # Proxy to Docker gateway + location / { + proxy_pass http://localhost:{{ docker_port }}; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # WebSocket support for Socket.io + location /socket.io/ { + proxy_pass http://localhost:{{ docker_port }}; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } + dest: /etc/nginx/sites-available/{{ domain }} + mode: '0644' + + - name: Enable Nginx site + file: + src: /etc/nginx/sites-available/{{ domain }} + dest: /etc/nginx/sites-enabled/{{ domain }} + state: link + + - name: Test Nginx configuration + command: nginx -t + changed_when: false + + - name: Reload Nginx + service: + name: nginx + state: reloaded + - name: Run Certbot to get HTTPS certificate include_role: name: certbot From f3e4b67896217d757712d43f128efad98e03ce3e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Nov 2025 07:39:59 +0000 Subject: [PATCH 10/12] refactor: Use separate Nginx site config file instead of inline Move Nginx site configuration to docker/peekaping/nginx-site.conf and copy it from there instead of defining inline in the Ansible playbook. This follows DRY principles and makes the configuration easier to maintain. Changes: - Create docker/peekaping/nginx-site.conf template - Copy nginx-site.conf from docker/peekaping/ instead of inline definition - Use replace task to substitute domain placeholder --- docker/peekaping/nginx-site.conf | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docker/peekaping/nginx-site.conf diff --git a/docker/peekaping/nginx-site.conf b/docker/peekaping/nginx-site.conf new file mode 100644 index 0000000..e14fa55 --- /dev/null +++ b/docker/peekaping/nginx-site.conf @@ -0,0 +1,26 @@ +server { + listen 80; + listen [::]:80; + server_name DOMAIN_PLACEHOLDER; + + # Proxy to Docker gateway + location / { + proxy_pass http://localhost:8383; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # WebSocket support for Socket.io + location /socket.io/ { + proxy_pass http://localhost:8383; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} From 10ae9a26fbb03e2598f8434de53440b33da9071b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Nov 2025 07:40:28 +0000 Subject: [PATCH 11/12] refactor: Update playbook to copy Nginx site config from file Replace inline Nginx configuration with file copy from docker/peekaping/nginx-site.conf --- ansible/playbooks/peekaping.yaml | 36 +++++++------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/ansible/playbooks/peekaping.yaml b/ansible/playbooks/peekaping.yaml index 6f192ed..f26cabb 100644 --- a/ansible/playbooks/peekaping.yaml +++ b/ansible/playbooks/peekaping.yaml @@ -154,38 +154,18 @@ delay: 10 until: peekaping_health.status == 200 - - name: Configure Nginx to proxy to Peekaping + - name: Copy Nginx site configuration to server copy: - content: | - server { - listen 80; - listen [::]:80; - server_name {{ domain }}; - - # Proxy to Docker gateway - location / { - proxy_pass http://localhost:{{ docker_port }}; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # WebSocket support for Socket.io - location /socket.io/ { - proxy_pass http://localhost:{{ docker_port }}; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - } + src: "{{ playbook_dir }}/../../docker/peekaping/nginx-site.conf" dest: /etc/nginx/sites-available/{{ domain }} mode: '0644' + - name: Replace domain placeholder in Nginx config + replace: + path: /etc/nginx/sites-available/{{ domain }} + regexp: 'DOMAIN_PLACEHOLDER' + replace: '{{ domain }}' + - name: Enable Nginx site file: src: /etc/nginx/sites-available/{{ domain }} From f5d374636417be6e0ae5a59d4e671b3d402ec8e6 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Nov 2025 07:45:30 +0000 Subject: [PATCH 12/12] feat: Disable default Nginx site to prevent fallback page Remove the default Nginx site so that accessing the server by IP address doesn't show the 'Welcome to nginx' page. Only the configured domain should respond with Peekaping. --- ansible/playbooks/peekaping.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ansible/playbooks/peekaping.yaml b/ansible/playbooks/peekaping.yaml index f26cabb..b96a4e2 100644 --- a/ansible/playbooks/peekaping.yaml +++ b/ansible/playbooks/peekaping.yaml @@ -154,6 +154,11 @@ delay: 10 until: peekaping_health.status == 200 + - name: Disable default Nginx site + file: + path: /etc/nginx/sites-enabled/default + state: absent + - name: Copy Nginx site configuration to server copy: src: "{{ playbook_dir }}/../../docker/peekaping/nginx-site.conf"