1kalin/afrexai-self-hosting-mastery/SKILL.md
Complete self-hosting and homelab operating system. Deploy, secure, monitor, and maintain self-hosted services with production-grade reliability. Use when setting up home servers, Docker infrastructure, reverse proxies, backups, monitoring, or evaluating self-hosted alternatives to SaaS.
npx skillsauth add openclaw/skills afrexai-self-hosting-masteryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Complete system for building and operating reliable self-hosted infrastructure — from first server to multi-node homelab.
server_profile:
name: ""
hardware:
cpu: "" # e.g., "Intel i5-12400" or "Raspberry Pi 5"
ram_gb: 0
storage:
- device: "" # e.g., "/dev/sda"
type: "" # ssd | hdd | nvme
size_gb: 0
role: "" # boot | data | backup
network: "" # 1gbe | 2.5gbe | 10gbe
os: "" # debian | ubuntu | proxmox | unraid | truenas
location: "" # home | closet | rack | colo | vps
power:
ups: false
wattage_idle: 0
wattage_load: 0
monthly_cost_estimate: "" # electricity
network:
public_ip: "" # static | dynamic | cgnat
domain: ""
dns_provider: "" # cloudflare | duckdns | custom
isp_ports_open: true # some ISPs block 80/443
goals:
- "" # media server, smart home, dev environment, etc.
budget_monthly: "" # electricity + domain + any VPS
| Budget | RAM | Storage | Good For | Example Hardware | |--------|-----|---------|----------|-----------------| | $0 | 4-8GB | 64GB+ | Pi-hole, AdGuard, small tools | Raspberry Pi 4/5 | | $50-150 | 8-16GB | 256GB+ | Docker host, 5-10 services | Used SFF PC (Dell Optiplex, Lenovo Tiny) | | $150-400 | 16-32GB | 1TB+ | NAS + services, media server | Mini PC (Intel NUC, Beelink) | | $400-800 | 32-64GB | 4TB+ | Full homelab, VMs + containers | Used enterprise (Dell R720, HP DL380) | | $800+ | 64GB+ | 10TB+ | Multi-node, Proxmox cluster | Multiple nodes, dedicated NAS |
Ask before self-hosting anything:
Always self-host: Password manager, DNS/ad-blocking, VPN, bookmarks, notes Usually self-host: Media server, file sync, photo backup, monitoring, git Think twice: Email (deliverability hell), calendar (sync complexity), chat (uptime expectations) Rarely worth it: Search engine (resource hungry), social media (no network effect)
| OS | Best For | Learning Curve | Notes | |----|----------|---------------|-------| | Debian 12 | Docker-only host | Low | Stable, minimal, just works | | Ubuntu Server 24.04 | Beginners, wide docs | Low | More packages, snap controversy | | Proxmox VE | VMs + containers | Medium | Free, enterprise features, ZFS | | Unraid | NAS + Docker + VMs | Medium | $59-129, great UI, parity array | | TrueNAS Scale | ZFS NAS + Docker | Medium | Free, ZFS-first, apps improving | | NixOS | Reproducible configs | High | Declarative, steep learning curve |
# Post-install essentials
# 1. Remove enterprise repo (if no subscription)
sed -i 's/^deb/#deb/' /etc/apt/sources.list.d/pve-enterprise.list
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-no-subscription.list
apt update && apt upgrade -y
# 2. Create a Docker LXC (lightweight container)
# Download template: Datacenter → Storage → CT Templates → Download → debian-12
# Create CT: 2 cores, 2GB RAM, 32GB disk, bridge vmbr0
# Inside CT: install Docker
apt install -y curl
curl -fsSL https://get.docker.com | sh
# 3. Enable IOMMU for GPU passthrough (if needed)
# Edit /etc/default/grub: GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on"
# update-grub && reboot
| Factor | VM | LXC | Docker | |--------|----|-----|--------| | Isolation | Full (own kernel) | Partial (shared kernel) | Process-level | | Overhead | High (1-2GB base) | Low (50-200MB) | Minimal | | Use when | Different OS, GPU passthrough, untrusted workloads | Dedicated service host, ZFS datasets | Most services | | Avoid when | RAM-constrained | Need Windows, custom kernel | Stateful databases (use LXC/VM) |
Rule: Docker for 90% of services. LXC for Docker hosts or isolated environments. VM for Windows, different kernel needs, or GPU passthrough.
/opt/stacks/ # or ~/docker/
├── traefik/
│ ├── docker-compose.yml
│ ├── .env
│ ├── config/
│ │ └── traefik.yml
│ └── data/
│ ├── acme.json # chmod 600
│ └── dynamic/
├── monitoring/
│ ├── docker-compose.yml
│ ├── .env
│ └── config/
├── media/
│ ├── docker-compose.yml
│ ├── .env
│ └── config/
├── productivity/
│ ├── docker-compose.yml
│ ├── .env
│ └── config/
└── scripts/
├── backup.sh
├── update-all.sh
└── health-check.sh
# Template: production-grade service
services:
app:
image: vendor/app:1.2.3 # ALWAYS pin version
container_name: app # Explicit name
restart: unless-stopped # Auto-restart
networks:
- proxy # Traefik network
- internal # Backend network
volumes:
- ./config:/config # Bind mount for config
- app-data:/data # Named volume for data
environment:
- TZ=Europe/London # Always set timezone
- PUID=1000 # Match host user
- PGID=1000
env_file:
- .env # Secrets in .env (gitignored)
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
memory: 512M # Prevent OOM cascades
security_opt:
- no-new-privileges:true # Security hardening
read_only: true # Where possible
tmpfs:
- /tmp
volumes:
app-data:
networks:
proxy:
external: true
internal:
:latest in production)restart: unless-stopped on all services.env files for secrets (never hardcode in compose)security_opt: no-new-privileges:trueread_only: true where possible + tmpfs for /tmpuser:)export DOCKER_CONTENT_TRUST=1docker system prune -afTZ environment variable on every container| Proxy | Best For | SSL | Config Style | Learning Curve | |-------|----------|-----|-------------|---------------| | Traefik | Docker-native, auto-discovery | Auto (ACME) | Labels + YAML | Medium | | Caddy | Simplicity, auto-SSL | Auto (built-in) | Caddyfile | Low | | Nginx Proxy Manager | GUI preference | Auto (UI) | Web UI | Very Low | | Nginx (manual) | Maximum control | Manual/certbot | Config files | High |
Recommendation: Traefik for Docker power users. Caddy for simplicity. NPM for beginners.
# traefik/config/traefik.yml
api:
dashboard: true
insecure: false
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
tls:
certResolver: letsencrypt
certificatesResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /data/acme.json
# Use DNS challenge if ISP blocks port 80
# dnsChallenge:
# provider: cloudflare
httpChallenge:
entryPoint: web
providers:
docker:
exposedByDefault: false # Explicit opt-in per service
network: proxy
file:
directory: /data/dynamic
watch: true
log:
level: WARN
accessLog:
filePath: /data/access.log
bufferingSize: 100
For CGNAT or ISPs blocking ports — expose services without opening firewall:
# cloudflared/docker-compose.yml
services:
cloudflared:
image: cloudflare/cloudflared:2024.1.0
container_name: cloudflared
restart: unless-stopped
command: tunnel run
environment:
- TUNNEL_TOKEN=${CF_TUNNEL_TOKEN}
networks:
- proxy
When to use Cloudflare Tunnel vs port forwarding:
| Service | Purpose | Image | RAM | Notes | |---------|---------|-------|-----|-------| | Traefik/Caddy | Reverse proxy + SSL | traefik:v3.0 | 64MB | Gateway to everything | | Pi-hole/AdGuard | DNS + ad blocking | pihole/pihole | 128MB | Network-wide ad blocking | | Authelia/Authentik | SSO + 2FA | authelia/authelia | 128MB | Protect services without built-in auth | | Uptime Kuma | Monitoring | louislam/uptime-kuma | 128MB | Know when things break | | Watchtower | Auto-updates | containrrr/watchtower | 32MB | Optional — some prefer manual |
| Service | Purpose | Alt | RAM | |---------|---------|-----|-----| | Vaultwarden | Password manager | Bitwarden | 64MB | | Nextcloud | File sync + office | Seafile (lighter) | 512MB | | Immich | Photo backup | PhotoPrism | 1-4GB | | Jellyfin | Media server | Plex (less free) | 512MB-2GB | | Paperless-ngx | Document management | - | 256MB | | Home Assistant | Smart home | - | 512MB |
| Service | Purpose | RAM | |---------|---------|-----| | Gitea/Forgejo | Git hosting | 256MB | | n8n | Workflow automation | 256MB | | Grafana + Prometheus | Metrics & dashboards | 512MB | | Tandoor | Recipe management | 256MB | | Mealie | Meal planning | 128MB | | Linkwarden/Hoarder | Bookmark manager | 256MB | | Stirling PDF | PDF tools | 512MB | | IT-Tools | Developer utilities | 64MB |
Total RAM needed ≈ OS base (1-2GB) + sum of service RAM + 20% headroom
Example 16GB server:
OS + Docker: 2 GB
Traefik: 0.1 GB
Pi-hole: 0.1 GB
Authelia: 0.1 GB
Uptime Kuma: 0.1 GB
Vaultwarden: 0.1 GB
Nextcloud: 0.5 GB
Immich: 2.0 GB
Jellyfin: 1.0 GB
Paperless: 0.3 GB
Home Assistant: 0.5 GB
──────────────────────
Total: 6.8 GB → 8.2 GB with headroom
Available: ~7.8 GB free for more services
Internet → Cloudflare DNS → Your Public IP → Router → Server
↓
Reverse Proxy (Traefik)
↓
┌──────────────────┼──────────────────┐
↓ ↓ ↓
app.domain.com files.domain.com media.domain.com
# Pi-hole/AdGuard: Local DNS rewrites
# Point *.home.example.com → 192.168.1.100 (server LAN IP)
# External: Cloudflare points to public IP
# Result: LAN traffic stays local, external goes through internet
| Solution | Type | Best For | Complexity | |----------|------|----------|-----------| | Tailscale | Mesh VPN | Easiest setup, multi-device | Very Low | | WireGuard | Point-to-point | Performance, full control | Medium | | Headscale | Self-hosted Tailscale | Privacy, no vendor lock | Medium-High |
Recommendation: Start with Tailscale (free for 3 users). Move to Headscale when you want full control.
# Default deny incoming
ufw default deny incoming
ufw default allow outgoing
# Allow SSH (change port from 22!)
ufw allow 2222/tcp comment 'SSH'
# Allow HTTP/HTTPS for reverse proxy
ufw allow 80/tcp comment 'HTTP redirect'
ufw allow 443/tcp comment 'HTTPS'
# Allow local network for discovery
ufw allow from 192.168.1.0/24 comment 'LAN'
# Enable
ufw enable
3 copies: Live data + Local backup + Remote backup
2 media: SSD/HDD (server) + External drive or NAS
1 offsite: Cloud (Backblaze B2, Wasabi) or second location
#!/bin/bash
# /opt/stacks/scripts/backup.sh
set -euo pipefail
BACKUP_DIR="/mnt/backup/docker"
STACKS_DIR="/opt/stacks"
DATE=$(date +%Y-%m-%d_%H%M)
RETENTION_DAYS=30
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; }
# 1. Stop services that need consistent backups
log "Stopping database services..."
cd "$STACKS_DIR/productivity" && docker compose stop db
# 2. Backup Docker volumes
log "Backing up volumes..."
for vol in $(docker volume ls -q); do
docker run --rm \
-v "$vol":/source:ro \
-v "$BACKUP_DIR/volumes":/backup \
alpine tar czf "/backup/${vol}_${DATE}.tar.gz" -C /source .
done
# 3. Backup compose files and configs
log "Backing up configs..."
tar czf "$BACKUP_DIR/configs/stacks_${DATE}.tar.gz" \
--exclude='*.log' \
--exclude='node_modules' \
"$STACKS_DIR"
# 4. Restart services
log "Restarting services..."
cd "$STACKS_DIR/productivity" && docker compose start db
# 5. Cleanup old backups
log "Cleaning up backups older than ${RETENTION_DAYS} days..."
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete
# 6. Sync to remote (Backblaze B2 example)
# rclone sync "$BACKUP_DIR" b2:my-backups/docker/ --transfers 4
# 7. Verify
BACKUP_SIZE=$(du -sh "$BACKUP_DIR" | cut -f1)
log "Backup complete. Total size: $BACKUP_SIZE"
# 8. Send notification (optional)
# curl -s "https://ntfy.sh/my-backups" -d "Backup complete: $BACKUP_SIZE"
| What | Frequency | Retention | Method | |------|-----------|-----------|--------| | Docker volumes | Daily 3 AM | 30 days | Script + cron | | Compose files + configs | Daily 3 AM | 90 days | Script + cron | | Database dumps | Every 6 hours | 7 days | pg_dump/mysqldump | | Full disk image | Monthly | 3 months | Clonezilla/dd | | Offsite sync | Daily 5 AM | 60 days | rclone to B2/Wasabi |
# monitoring/docker-compose.yml
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
volumes:
- uptime-data:/app/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.uptime.rule=Host(`status.example.com`)"
prometheus:
image: prom/prometheus:v2.49.0
container_name: prometheus
restart: unless-stopped
volumes:
- ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
grafana:
image: grafana/grafana:10.3.0
container_name: grafana
restart: unless-stopped
volumes:
- grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
node-exporter:
image: prom/node-exporter:v1.7.0
container_name: node-exporter
restart: unless-stopped
pid: host
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.0
container_name: cadvisor
restart: unless-stopped
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
volumes:
uptime-data:
prometheus-data:
grafana-data:
| Metric | Warning | Critical | Action | |--------|---------|----------|--------| | Disk usage | >80% | >90% | Cleanup or expand | | RAM usage | >85% | >95% | Identify memory leak, add RAM | | CPU sustained | >80% 5min | >95% 5min | Check runaway process | | Container restart | >2/hour | >5/hour | Check logs, fix root cause | | SSL cert expiry | <14 days | <3 days | Renew cert | | Backup age | >26 hours | >48 hours | Check backup script/cron | | Service down | >2 min | >10 min | Investigate, restart |
| Channel | Service | Best For | |---------|---------|----------| | Push notification | ntfy.sh (self-hosted) | Mobile alerts | | Chat | Discord/Slack webhook | Team alerts | | Email | Uptime Kuma built-in | Formal notifications | | Dashboard | Grafana + Uptime Kuma | Visual monitoring |
# 1. SSH hardening
# /etc/ssh/sshd_config
Port 2222 # Change default port
PermitRootLogin no # No root SSH
PasswordAuthentication no # Key-only
MaxAuthTries 3
AllowUsers yourusername
# 2. Install fail2ban
apt install fail2ban -y
systemctl enable fail2ban
# 3. Automatic security updates
apt install unattended-upgrades -y
dpkg-reconfigure -plow unattended-upgrades
# 4. Disable unused services
systemctl list-unit-files --state=enabled
# Disable anything you don't need
Internet → Traefik → Authelia/Authentik → Service
↓
Check: authenticated?
Yes → Forward to service
No → Redirect to login page + 2FA
Authelia (lightweight, YAML config) — good for smaller setups Authentik (full IdP, web UI) — good for many users/services, SAML/OIDC
| Dimension | Weight | Score Guide | |-----------|--------|-------------| | SSH hardened (keys, non-root, non-22) | 15 | 0=default, 15=fully hardened | | Firewall active (deny-by-default) | 15 | 0=none, 15=UFW/iptables configured | | Reverse proxy (no direct port exposure) | 15 | 0=ports exposed, 15=all behind proxy | | SSL/TLS on all services | 10 | 0=HTTP, 10=HTTPS everywhere | | Auth on all public services | 15 | 0=open, 15=SSO/2FA on everything | | Container security (non-root, limits) | 10 | 0=default, 10=hardened | | Auto-updates enabled | 10 | 0=manual, 10=automated | | Secrets management (.env, not hardcoded) | 10 | 0=in compose, 10=.env + restricted perms |
Score: 0-40 = Vulnerable, 41-70 = Acceptable, 71-90 = Good, 91-100 = Hardened
Option A: Manual (Recommended for critical services)
# Update script: /opt/stacks/scripts/update-all.sh
#!/bin/bash
set -euo pipefail
STACKS_DIR="/opt/stacks"
LOG="/var/log/docker-updates.log"
for stack in "$STACKS_DIR"/*/; do
if [ -f "$stack/docker-compose.yml" ]; then
echo "[$(date)] Updating $(basename $stack)..." | tee -a "$LOG"
cd "$stack"
docker compose pull 2>&1 | tee -a "$LOG"
docker compose up -d 2>&1 | tee -a "$LOG"
fi
done
docker image prune -f | tee -a "$LOG"
echo "[$(date)] Update complete" | tee -a "$LOG"
Option B: Watchtower (Automated — use with caution)
services:
watchtower:
image: containrrr/watchtower:1.7.1
container_name: watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WATCHTOWER_SCHEDULE=0 0 4 * * MON # Monday 4 AM
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_NOTIFICATIONS=shoutrrr
- WATCHTOWER_NOTIFICATION_URL=discord://webhook
- WATCHTOWER_LABEL_ENABLE=true # Only update labeled containers
# Add label to containers: com.centurylinklabs.watchtower.enable=true
df -h)docker ps --filter health=unhealthy)fail2ban-client status)docker logs --since 7d <container>)docker system prune -f)apt update && apt upgrade)┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Node 1 │ │ Node 2 │ │ Node 3 │
│ (Proxy/DNS) │────│ (Services) │────│ (NAS) │
│ Traefik │ │ Apps │ │ TrueNAS │
│ Pi-hole │ │ Databases │ │ NFS/SMB │
│ Authelia │ │ Media │ │ Backup │
└─────────────┘ └─────────────┘ └─────────────┘
↑ ↑ ↑
└───────── Tailscale Mesh ──────────────┘
# Shared fragments
include:
- path: ../common/traefik-labels.yml
- path: ../common/logging.yml
services:
app:
# inherits common configs
homelab-configs/ # Git repo
├── .github/
│ └── workflows/
│ └── deploy.yml # CI: lint + push to server
├── stacks/
│ ├── traefik/
│ ├── monitoring/
│ └── media/
├── scripts/
└── README.md
Workflow: Edit compose locally → commit → push → CI deploys to server
Tools: Flux/ArgoCD (overkill), or simple git pull && docker compose up -d via webhook
| Component | Solution | Cost | |-----------|----------|------| | Power | UPS (APC Back-UPS 600VA+) | $60-150 | | Storage | RAID1/ZFS mirror (not RAID0!) | 2x disk cost | | Network | Dual NIC, managed switch | $30-100 | | Server | Second node (cold spare or active) | $100-400 |
Rule: RAID is NOT backup. It protects against disk failure only, not ransomware/deletion/corruption.
Service not accessible?
├── Can you ping the server? → No → Network/firewall issue
├── Is the container running? (`docker ps`) → No → Check logs: `docker logs <name>`
├── Is the port exposed? (`docker port <name>`) → No → Check compose ports/networks
├── Is Traefik routing? (Check Traefik dashboard) → No → Check labels, network
├── Is DNS resolving? (`dig app.example.com`) → No → Check DNS provider
└── SSL error? → Check acme.json permissions (chmod 600), cert resolver logs
# Container not starting
docker logs <name> --tail 50
docker inspect <name> | jq '.[0].State'
# Network issues
docker network ls
docker network inspect <network>
docker exec <name> ping other-container
# Resource issues
docker stats # Live resource usage
docker system df # Disk usage
docker volume ls -f dangling=true # Orphaned volumes
# Nuclear options (use carefully)
docker compose down && docker compose up -d # Full restart
docker system prune -af --volumes # Clean EVERYTHING
| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| Slow file access | HDD for database | Move DB to SSD |
| High CPU idle | Monitoring too frequent | Increase scrape intervals |
| OOM kills | No memory limits | Set deploy.resources.limits.memory |
| Slow Nextcloud | Missing Redis cache | Add Redis container |
| Jellyfin buffering | No hardware transcoding | Enable GPU passthrough |
| Slow Docker builds | No layer caching | Use multi-stage + .dockerignore |
services:
vaultwarden:
image: vaultwarden/server:1.30.5
container_name: vaultwarden
restart: unless-stopped
volumes:
- vaultwarden-data:/data
environment:
- SIGNUPS_ALLOWED=false # Disable after creating your account
- WEBSOCKET_ENABLED=true
- ADMIN_TOKEN=${ADMIN_TOKEN} # Generate: openssl rand -base64 48
labels:
- "traefik.enable=true"
- "traefik.http.routers.vault.rule=Host(`vault.example.com`)"
# Use their official docker-compose.yml from:
# https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
# Key settings:
# - Set UPLOAD_LOCATION to a large storage mount
# - Enable hardware transcoding if GPU available
# - Set IMMICH_MACHINE_LEARNING_URL for face detection
services:
paperless:
image: ghcr.io/paperless-ngx/paperless-ngx:2.4
container_name: paperless
restart: unless-stopped
volumes:
- paperless-data:/usr/src/paperless/data
- paperless-media:/usr/src/paperless/media
- ./consume:/usr/src/paperless/consume # Drop PDFs here
- ./export:/usr/src/paperless/export
environment:
- PAPERLESS_OCR_LANGUAGE=eng
- PAPERLESS_TIME_ZONE=Europe/London
- PAPERLESS_ADMIN_USER=${ADMIN_USER}
- PAPERLESS_ADMIN_PASSWORD=${ADMIN_PASS}
| Dimension | Weight | 0 (Poor) | 50 (Decent) | 100 (Excellent) | |-----------|--------|----------|-------------|-----------------| | Security | 20% | Default passwords, open ports | Firewall + SSL | Hardened SSH, SSO/2FA, no-new-privileges | | Backups | 20% | None | Local only, untested | 3-2-1, automated, verified monthly | | Monitoring | 15% | None | Uptime Kuma only | Full stack: metrics + logs + alerts | | Documentation | 10% | Nothing written | README per stack | GitOps, full runbook, diagrams | | Updates | 10% | Never updated | Manual quarterly | Scheduled weekly, changelogs reviewed | | Reliability | 10% | Frequent crashes | Mostly stable | UPS, auto-restart, health checks | | Performance | 10% | Slow, OOM kills | Adequate | Resource limits, SSD, HW transcoding | | Scalability | 5% | Single machine, no plan | Compose organized | Multi-node ready, IaC |
| # | Mistake | Fix |
|---|---------|-----|
| 1 | Using :latest tag | Pin versions: image:1.2.3 |
| 2 | No backups | 3-2-1 backup rule, test restores |
| 3 | Exposing ports directly | Everything behind reverse proxy |
| 4 | Default passwords | Change immediately, use password manager |
| 5 | No monitoring | Uptime Kuma minimum, Grafana for depth |
| 6 | RAID = backup mentality | RAID protects disks, not data |
| 7 | Over-engineering day 1 | Start small, add complexity as needed |
| 8 | No documentation | Document every service, every port, every cron |
| 9 | Ignoring updates | Security patches matter, schedule updates |
| 10 | Running as root | Non-root containers, restricted SSH |
| Say | Agent Does | |-----|-----------| | "Set up a new service" | Guide through compose file creation with security best practices | | "Audit my homelab security" | Run through security scoring checklist | | "Plan my backup strategy" | Design 3-2-1 backup plan for your setup | | "What should I self-host?" | Assess needs and recommend services by tier | | "My container keeps crashing" | Walk through troubleshooting decision tree | | "Help me set up Traefik" | Generate production Traefik config with SSL | | "Compare NAS options" | Compare TrueNAS vs Unraid vs DIY for your needs | | "Optimize my Docker setup" | Review compose files for security and performance | | "Set up monitoring" | Deploy Uptime Kuma + Prometheus + Grafana stack | | "Plan a hardware upgrade" | Assess current usage, recommend hardware by budget | | "Migrate from cloud to self-hosted" | Plan migration with data export and service mapping | | "Set up remote access" | Compare and deploy VPN/Tailscale for secure remote access |
tools
Use when the user wants to connect to, test, or use the McDonalds service at mcp.mcd.cn, including checking authentication, probing MCP endpoints, listing tools, or calling McDonalds MCP tools through a reusable local CLI.
development
Web scraping platform — Twitter/X data, Vinted marketplace, and general web scraping API
development
SlowMist AI Agent Security Review — comprehensive security framework for skills, repositories, URLs, on-chain addresses, and products (Claude Code version)
data-ai
去除中文文本中的 AI 写作痕迹,使其读起来自然。基于维基百科 AI 写作特征指南,检测 24 种 AI 模式。触发词:humanizer-cn、去除 AI 痕迹、去除 AI 写作痕迹、中文文本人性化。