Guia operacional para proteger, copiar, restaurar e auditar os projetos rodando no servidor local da Dark n Black.
Última atualização: 2026-10-11
Servidor:dnb-local
IP LAN:192.168.50.99
Usuário Linux usado nos exemplos:dwoond
Base principal:/srv/homelab
Documento complementar:documentacao-homelab-dnb-servidor-projetos.md
Este documento existe para responder três perguntas:
Ele também define regras mínimas de segurança para evitar perda de dados, vazamento de secrets, exposição indevida de serviços e quebras acidentais em stacks que já estão funcionando.
Estrutura-base do homelab:
/srv/homelab
├── apps
│ ├── atlas
│ ├── ledger
│ └── hub-metrics
│
├── supabase
│ ├── atlas
│ │ └── docker
│ ├── ledger
│ │ └── docker
│ └── hub-metrics
│ └── docker
│
├── proxy
│ ├── docker-compose.yml
│ └── Caddyfile
│
├── infra
│ └── cloudflared
│ ├── docker-compose.yml
│ └── .env
│
├── monitoring
├── backups
├── scripts
└── logs
Serviços atuais mais importantes:
| Área | Caminho | Observação |
|---|---|---|
| Apps Next.js | /srv/homelab/apps/<projeto> | Código, Dockerfile, compose e env de app |
| Supabase por projeto | /srv/homelab/supabase/<projeto>/docker | Banco, Auth, Storage, Studio e APIs daquele projeto |
| Proxy | /srv/homelab/proxy | Caddy e roteamento central |
| Tunnel | /srv/homelab/infra/cloudflared | Cloudflare Tunnel para acesso externo |
| Backups | /srv/homelab/backups | Destino local de dumps e pacotes |
| Scripts | /srv/homelab/scripts | Automatizações de backup, restore e auditoria |
| Logs | /srv/homelab/logs | Logs operacionais do homelab |
Regra de arquitetura:
Internet → Cloudflare Tunnel → Caddy → apps/Supabase
Evitar:
Internet → app direto
Internet → Supabase direto
Internet → Postgres direto
Nem tudo tem o mesmo valor. A ordem de prioridade é esta:
| Prioridade | Item | Precisa backup? | Por quê |
|---|---|---|---|
| Crítica | .env, secrets, senhas, JWT, service role | Sim, criptografado | Sem isso, a restauração pode ficar impossível ou insegura |
| Crítica | Banco Postgres de cada Supabase | Sim | Dados reais do projeto |
| Alta | Storage do Supabase | Sim, quando usado | Uploads, arquivos, imagens e documentos |
| Alta | Caddyfile e compose do proxy | Sim | Rotas, domínios e reverse proxy |
| Alta | Config do cloudflared | Sim, criptografado | Tunnel e acesso externo |
| Alta | docker-compose.yml dos apps e Supabase | Sim | Permite recriar serviços corretamente |
| Média | Código dos apps | Git é a fonte principal | Backup local é opcional se estiver tudo no Git |
| Média | Scripts internos | Sim | Automatizações e runbooks |
| Baixa | Logs | Opcional | Úteis para auditoria, mas podem ser grandes |
| Baixa | Imagens Docker | Não | Podem ser baixadas/rebuildadas |
| Baixa | node_modules, .next, caches | Não | Gerados novamente no build |
Usar a regra prática 3-2-1:
3 cópias dos dados importantes
2 tipos de mídia/destino
1 cópia fora do servidor
Para o homelab:
Cópia 1: dados vivos no servidor
Cópia 2: backup local em /srv/homelab/backups
Cópia 3: backup externo/offsite criptografado
| Tipo | Frequência | Retenção sugerida |
|---|---|---|
| Dump Postgres | Diário | 14 a 30 dias |
Backup de .env/configs | Diário ou a cada alteração | 30 a 90 dias |
| Storage Supabase | Diário, se usado | 14 a 30 dias |
| Backup completo antes de update | Sempre antes de update grande | Manter por 30 dias |
| Teste de restore | Mensal | Registrar resultado |
| Backup offsite | Diário ou semanal | 30 a 90 dias |
Secrets nunca devem ficar apenas em um lugar.
Itens que precisam de cópia segura:
POSTGRES_PASSWORD
JWT_SECRET
ANON_KEY
SERVICE_ROLE_KEY
DASHBOARD_PASSWORD
CRON_SECRET
Tokens do Cloudflare
Tokens de GitHub runner
Chaves de APIs externas
.env.production.local dos apps
.env dos Supabases
Regra:
Secrets podem ficar no servidor em produção.
Secrets podem ficar em backup criptografado.
Secrets não podem ir para Git.
Secrets não devem ser colados em chats, issues ou logs.
Criar estrutura:
sudo mkdir -p /srv/homelab/backups/{local,offsite-staging,restore-tests,logs}
sudo mkdir -p /srv/homelab/scripts
sudo mkdir -p /srv/homelab/logs
sudo chown -R dwoond:dwoond /srv/homelab/backups /srv/homelab/scripts /srv/homelab/logs
chmod 700 /srv/homelab/backups
chmod 700 /srv/homelab/scripts
Estrutura final:
/srv/homelab/backups
├── local
│ ├── atlas
│ ├── ledger
│ ├── hub-metrics
│ ├── proxy
│ └── infra
│
├── offsite-staging
├── restore-tests
└── logs
Exemplo usando hub-metrics.
cd /srv/homelab/supabase/hub-metrics/docker
docker compose ps
O ideal é que db, auth, rest, kong, storage, studio, meta e pooler estejam healthy ou up.
PROJECT="hub-metrics"
DATE="$(date +%Y%m%d-%H%M%S)"
OUT="/srv/homelab/backups/local/$PROJECT/$DATE"
mkdir -p "$OUT"
chmod 700 "$OUT"
Formato recomendado: custom (.dump), pois é melhor para restore com pg_restore.
docker compose exec -T db bash -lc 'PGPASSWORD="$POSTGRES_PASSWORD" pg_dump -h 127.0.0.1 -U postgres -d postgres -Fc --no-owner --no-privileges' > "$OUT/postgres.dump"
Verificar tamanho:
ls -lh "$OUT/postgres.dump"
Se o arquivo tiver poucos bytes ou tamanho zero, o backup falhou.
O .env contém secrets. Então existem duas opções:
tar -czf "$OUT/supabase-config.tar.gz" \
docker-compose.yml \
.env \
volumes \
--exclude='volumes/db/data' \
--exclude='volumes/storage'
chmod 600 "$OUT/supabase-config.tar.gz"
tar -czf - \
docker-compose.yml \
.env \
volumes \
--exclude='volumes/db/data' \
--exclude='volumes/storage' \
| gpg --symmetric --cipher-algo AES256 -o "$OUT/supabase-config.tar.gz.gpg"
chmod 600 "$OUT/supabase-config.tar.gz.gpg"
Use a opção criptografada para qualquer cópia que saia do servidor.
Primeiro verifique se existe storage local:
ls -la volumes/storage 2>/dev/null || true
Se existir e tiver arquivos:
tar -czf "$OUT/storage.tar.gz" volumes/storage
chmod 600 "$OUT/storage.tar.gz"
Se o storage estiver em outro volume Docker, descubra com:
docker compose config | grep -n "storage\|volumes" -A5 -B5
Criar arquivo:
nano /srv/homelab/scripts/backup-supabase-project.sh
Conteúdo:
#!/usr/bin/env bash
set -euo pipefail
PROJECT="${1:-}"
if [ -z "$PROJECT" ]; then
echo "Uso: $0 <projeto>"
echo "Exemplo: $0 hub-metrics"
exit 1
fi
BASE="/srv/homelab"
STACK_DIR="$BASE/supabase/$PROJECT/docker"
DATE="$(date +%Y%m%d-%H%M%S)"
OUT="$BASE/backups/local/$PROJECT/$DATE"
LOG_DIR="$BASE/logs"
if [ ! -d "$STACK_DIR" ]; then
echo "Stack não encontrada: $STACK_DIR"
exit 1
fi
mkdir -p "$OUT" "$LOG_DIR"
chmod 700 "$OUT"
cd "$STACK_DIR"
echo "[$(date -Is)] Iniciando backup do Supabase: $PROJECT"
echo "[$(date -Is)] Verificando containers..."
docker compose ps > "$OUT/docker-compose-ps.txt"
echo "[$(date -Is)] Backup do Postgres..."
docker compose exec -T db bash -lc 'PGPASSWORD="$POSTGRES_PASSWORD" pg_dump -h 127.0.0.1 -U postgres -d postgres -Fc --no-owner --no-privileges' > "$OUT/postgres.dump"
if [ ! -s "$OUT/postgres.dump" ]; then
echo "ERRO: postgres.dump vazio ou inexistente"
exit 1
fi
echo "[$(date -Is)] Backup de configurações..."
tar -czf "$OUT/supabase-config.tar.gz" \
docker-compose.yml \
.env \
volumes \
--exclude='volumes/db/data' \
--exclude='volumes/storage' \
2>/dev/null || true
chmod 600 "$OUT/supabase-config.tar.gz" || true
if [ -d "volumes/storage" ]; then
echo "[$(date -Is)] Backup do Storage..."
tar -czf "$OUT/storage.tar.gz" volumes/storage
chmod 600 "$OUT/storage.tar.gz"
fi
sha256sum "$OUT"/* > "$OUT/SHA256SUMS.txt" 2>/dev/null || true
cat > "$OUT/README.txt" <<EOF_README
Projeto: $PROJECT
Data: $DATE
Stack: $STACK_DIR
Conteúdo:
- postgres.dump: dump custom do Postgres
- supabase-config.tar.gz: docker-compose.yml, .env e volumes de configuração sem data dir
- storage.tar.gz: storage local, se existir
- docker-compose-ps.txt: estado dos containers no momento do backup
- SHA256SUMS.txt: checksums dos arquivos
EOF_README
echo "[$(date -Is)] Backup concluído: $OUT"
Permissão:
chmod +x /srv/homelab/scripts/backup-supabase-project.sh
Teste:
/srv/homelab/scripts/backup-supabase-project.sh hub-metrics
Para Atlas e Ledger:
/srv/homelab/scripts/backup-supabase-project.sh atlas
/srv/homelab/scripts/backup-supabase-project.sh ledger
Este script salva arquivos operacionais importantes, incluindo envs dos apps.
Criar:
nano /srv/homelab/scripts/backup-homelab-configs.sh
Conteúdo:
#!/usr/bin/env bash
set -euo pipefail
BASE="/srv/homelab"
DATE="$(date +%Y%m%d-%H%M%S)"
OUT="$BASE/backups/local/configs/$DATE"
mkdir -p "$OUT"
chmod 700 "$OUT"
cd "$BASE"
echo "[$(date -Is)] Backup de configs do homelab..."
tar -czf "$OUT/apps-configs.tar.gz" \
apps \
--exclude='apps/*/.next' \
--exclude='apps/*/node_modules' \
--exclude='apps/*/.git' \
--exclude='apps/*/dist' \
--exclude='apps/*/build' \
2>/dev/null || true
if [ -d "$BASE/proxy" ]; then
tar -czf "$OUT/proxy.tar.gz" proxy
fi
if [ -d "$BASE/infra" ]; then
tar -czf "$OUT/infra.tar.gz" infra
fi
if [ -d "$BASE/scripts" ]; then
tar -czf "$OUT/scripts.tar.gz" scripts
fi
chmod 600 "$OUT"/*.tar.gz 2>/dev/null || true
sha256sum "$OUT"/* > "$OUT/SHA256SUMS.txt" 2>/dev/null || true
echo "[$(date -Is)] Backup de configs concluído: $OUT"
Permissão:
chmod +x /srv/homelab/scripts/backup-homelab-configs.sh
Teste:
/srv/homelab/scripts/backup-homelab-configs.sh
Criar:
nano /srv/homelab/scripts/backup-homelab-daily.sh
Conteúdo:
#!/usr/bin/env bash
set -euo pipefail
BASE="/srv/homelab"
LOG="$BASE/logs/backup-daily.log"
mkdir -p "$BASE/logs"
echo "==============================" >> "$LOG"
echo "[$(date -Is)] Início backup diário" >> "$LOG"
PROJECTS=("atlas" "ledger" "hub-metrics")
for PROJECT in "${PROJECTS[@]}"; do
if [ -d "$BASE/supabase/$PROJECT/docker" ]; then
echo "[$(date -Is)] Backup Supabase: $PROJECT" >> "$LOG"
"$BASE/scripts/backup-supabase-project.sh" "$PROJECT" >> "$LOG" 2>&1 || {
echo "[$(date -Is)] ERRO no backup do projeto: $PROJECT" >> "$LOG"
}
else
echo "[$(date -Is)] Ignorando projeto sem stack: $PROJECT" >> "$LOG"
fi
done
echo "[$(date -Is)] Backup configs" >> "$LOG"
"$BASE/scripts/backup-homelab-configs.sh" >> "$LOG" 2>&1 || {
echo "[$(date -Is)] ERRO no backup de configs" >> "$LOG"
}
echo "[$(date -Is)] Fim backup diário" >> "$LOG"
Permissão:
chmod +x /srv/homelab/scripts/backup-homelab-daily.sh
Teste:
/srv/homelab/scripts/backup-homelab-daily.sh
Ver log:
tail -120 /srv/homelab/logs/backup-daily.log
Editar cron do usuário dwoond:
crontab -e
Adicionar:
0 3 * * * /srv/homelab/scripts/backup-homelab-daily.sh >> /srv/homelab/logs/cron-backup.log 2>&1
Isso roda diariamente às 03:00.
Verificar cron instalado:
crontab -l
Conferir logs no dia seguinte:
tail -120 /srv/homelab/logs/backup-daily.log
tail -120 /srv/homelab/logs/cron-backup.log
Antes de apagar, sempre faça dry-run.
find /srv/homelab/backups/local -type f -mtime +30 -print
find /srv/homelab/backups/local -type f -mtime +30 -delete
find /srv/homelab/backups/local -type d -empty -delete
Criar:
nano /srv/homelab/scripts/backup-retention.sh
Conteúdo:
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR="/srv/homelab/backups/local"
LOG="/srv/homelab/logs/backup-retention.log"
RETENTION_DAYS="30"
echo "[$(date -Is)] Limpando backups com mais de $RETENTION_DAYS dias" >> "$LOG"
find "$BACKUP_DIR" -type f -mtime +"$RETENTION_DAYS" -print -delete >> "$LOG" 2>&1
find "$BACKUP_DIR" -type d -empty -delete >> "$LOG" 2>&1
echo "[$(date -Is)] Limpeza concluída" >> "$LOG"
Permissão:
chmod +x /srv/homelab/scripts/backup-retention.sh
Cron semanal:
30 3 * * 0 /srv/homelab/scripts/backup-retention.sh >> /srv/homelab/logs/cron-retention.log 2>&1
Backup local não basta. Se o notebook/SSD morrer, o backup local morre junto.
Destinos possíveis:
Google Drive
Dropbox
Backblaze B2
Cloudflare R2
Outro servidor via SSH/rsync
HD externo criptografado
Nunca envie para offsite sem uma destas proteções:
1. Criptografia no arquivo antes do upload
2. Rclone crypt
3. Bucket com criptografia forte + acesso restrito + secrets fora do repo
Usar:
/srv/homelab/backups/offsite-staging
Exemplo:
DATE="$(date +%Y%m%d-%H%M%S)"
SRC="/srv/homelab/backups/local"
DEST="/srv/homelab/backups/offsite-staging/homelab-backup-$DATE.tar.gz"
tar -czf "$DEST" -C "$SRC" .
chmod 600 "$DEST"
Criptografar com GPG simétrico:
gpg --symmetric --cipher-algo AES256 "$DEST"
rm -f "$DEST"
Resultado:
homelab-backup-YYYYMMDD-HHMMSS.tar.gz.gpg
Esse arquivo pode ir para Google Drive, R2, B2, HD externo etc.
Atenção: restore sobrescreve dados. Antes de restaurar, faça um backup novo do estado atual.
cd /srv/homelab/supabase/hub-metrics/docker
/srv/homelab/scripts/backup-supabase-project.sh hub-metrics
cd /srv/homelab/apps/hub-metrics
docker compose stop hub-metrics
Voltar para a stack Supabase:
cd /srv/homelab/supabase/hub-metrics/docker
Restaurar:
BACKUP_FILE="/srv/homelab/backups/local/hub-metrics/COLE_A_DATA/postgres.dump"
docker compose exec -T db bash -lc 'PGPASSWORD="$POSTGRES_PASSWORD" pg_restore -h 127.0.0.1 -U postgres -d postgres --clean --if-exists --no-owner --no-privileges' < "$BACKUP_FILE"
cd /srv/homelab/supabase/hub-metrics/docker
docker compose up -d
docker compose ps
Depois:
cd /srv/homelab/apps/hub-metrics
docker compose up -d
curl -I http://127.0.0.1:8003
curl -I -H "Host: metrics.darknblack.com" http://127.0.0.1
Se backup não criptografado:
cd /srv/homelab/supabase/hub-metrics/docker
tar -xzf /srv/homelab/backups/local/hub-metrics/COLE_A_DATA/supabase-config.tar.gz -C /tmp/restore-supabase-config
Compare antes de substituir:
diff -u .env /tmp/restore-supabase-config/.env || true
diff -u docker-compose.yml /tmp/restore-supabase-config/docker-compose.yml || true
Nunca sobrescreva .env sem comparar.
gpg -d supabase-config.tar.gz.gpg > supabase-config.tar.gz
Depois extraia e compare.
Código deve vir do Git:
cd /srv/homelab/apps
git clone git@github.com:SEU_USUARIO/hub-metrics.git hub-metrics
Restaurar .env.production.local do backup de configs:
cd /srv/homelab/apps/hub-metrics
cp /CAMINHO/RESTAURADO/apps/hub-metrics/.env.production.local .env.production.local
ln -sf .env.production.local .env
chmod 600 .env.production.local
Subir:
docker compose up -d --build
Pode ser público, via Cloudflare Tunnel e Caddy:
metrics.darknblack.com → app público
api-metrics.darknblack.com → API Supabase necessária ao app
Preferencialmente protegido por Cloudflare Access, VPN ou rede local:
Supabase Studio
Netdata
Dashboards internos
Rotas administrativas
Endpoints de cron
Painéis de deploy
Nunca expor diretamente:
Postgres
Docker socket
SSH aberto para a internet
Portainer sem proteção forte
Arquivos .env
Backups
Diretórios /srv/homelab
Painéis admin sem autenticação forte
sudo systemctl status ssh
Usar chave SSH e evitar senha.
Arquivo:
sudo nano /etc/ssh/sshd_config
Recomendações:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Antes de aplicar PasswordAuthentication no, confirme que o login por chave está funcionando.
Reiniciar SSH:
sudo systemctl restart ssh
Teste em outro terminal antes de fechar o atual:
ssh dwoond@192.168.50.99
Se usar Cloudflare Tunnel para entrada pública, o servidor não precisa abrir portas no roteador.
Ver status:
sudo ufw status verbose
Política segura inicial:
sudo ufw default deny incoming
sudo ufw default allow outgoing
Permitir SSH pela LAN:
sudo ufw allow from 192.168.50.0/24 to any port 22 proto tcp
Permitir HTTP pela LAN para Caddy local:
sudo ufw allow from 192.168.50.0/24 to any port 80 proto tcp
Se usar Tailscale:
sudo ufw allow in on tailscale0
Ativar:
sudo ufw enable
sudo ufw status verbose
Atenção: aplique firewall com cuidado. Não faça isso sem acesso físico ou sem uma sessão SSH alternativa aberta.
Regras:
Não montar /var/run/docker.sock em apps comuns.
Não usar privileged: true sem necessidade extrema.
Não publicar ports nos apps; usar expose + Caddy.
Não colocar secrets no Dockerfile.
Não commitar .env.
Não usar latest em serviços críticos quando possível.
Manter cada projeto em compose próprio.
Ver containers com portas publicadas:
docker ps --format "table {{.Names}}\t{{.Ports}}"
Idealmente, apps Next.js devem aparecer só com:
3000/tcp
E não:
0.0.0.0:3000->3000/tcp
Supabase publica portas específicas por stack:
Atlas: 8001, 8444, Postgres conforme stack
Ledger: 8002, 8445, 54322, 6544
Hub Metrics: 8003, 8446, 54323, 6545
Essas portas devem ficar restritas à LAN/host, não abertas no roteador.
Bloco global atual recomendado:
{
auto_https off
servers {
trusted_proxies static private_ranges
}
}
Esse bloco precisa ser o primeiro do Caddyfile.
Validação:
cd /srv/homelab/proxy
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile
Reload:
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
Para apps públicos, considerar:
metrics.darknblack.com {
header {
X-Content-Type-Options nosniff
X-Frame-Options DENY
Referrer-Policy strict-origin-when-cross-origin
Permissions-Policy "geolocation=(), microphone=(), camera=()"
}
reverse_proxy hub-metrics:3000
}
Cuidado com Content-Security-Policy: pode quebrar Next.js, Supabase Auth, fontes, imagens e scripts externos se aplicada sem teste.
Para Studio, Netdata e painéis internos, preferir Cloudflare Access no domínio público ou manter apenas local.
Exemplos de domínios que devem ser protegidos:
studio-hub-metrics.local
monitor.local
studio.darknblack.com, se existir
Princípio:
Tunnel aponta para Caddy.
Caddy decide para onde cada domínio vai.
Não apontar o tunnel diretamente para Postgres, Docker, Netdata ou painel admin sem proteção.
metrics.darknblack.com → Caddy → hub-metrics:3000
api-metrics.darknblack.com → Caddy → host.docker.internal:8003
Para app público:
HTTPS ativo
WAF básico
Bot fight/challenges com cuidado
Cache desativado para rotas dinâmicas sensíveis
Para admin:
Cloudflare Access obrigatório
Login por e-mail permitido apenas para você/equipe
MFA habilitado na conta Cloudflare
Tokens do tunnel e API tokens:
Não colocar no Git
Não colocar no Caddyfile
Guardar em /srv/homelab/infra/cloudflared/.env com chmod 600
Backup apenas criptografado
Rotacionar se houver suspeita de vazamento
No Supabase do projeto:
cd /srv/homelab/supabase/hub-metrics/docker
Ver variável usada:
grep -Rni "SIGNUP\|DISABLE_SIGNUP\|ENABLE.*SIGNUP\|GOTRUE.*SIGNUP" .env docker-compose.yml volumes 2>/dev/null
Se existir DISABLE_SIGNUP:
grep -q '^DISABLE_SIGNUP=' .env \
&& sed -i 's|^DISABLE_SIGNUP=.*|DISABLE_SIGNUP=true|' .env \
|| echo 'DISABLE_SIGNUP=true' >> .env
Se existir ENABLE_EMAIL_SIGNUP:
grep -q '^ENABLE_EMAIL_SIGNUP=' .env \
&& sed -i 's|^ENABLE_EMAIL_SIGNUP=.*|ENABLE_EMAIL_SIGNUP=false|' .env \
|| echo 'ENABLE_EMAIL_SIGNUP=false' >> .env
Aplicar:
docker compose up -d auth
No Supabase Hub Metrics:
SUPABASE_PUBLIC_URL=https://api-metrics.darknblack.com
API_EXTERNAL_URL=https://api-metrics.darknblack.com/auth/v1
SITE_URL=https://metrics.darknblack.com
Aplicar sem nano:
cd /srv/homelab/supabase/hub-metrics/docker
cp .env ".env.bak-public-urls-$(date +%Y%m%d-%H%M%S)"
sed -i 's|^SUPABASE_PUBLIC_URL=.*|SUPABASE_PUBLIC_URL=https://api-metrics.darknblack.com|' .env
sed -i 's|^API_EXTERNAL_URL=.*|API_EXTERNAL_URL=https://api-metrics.darknblack.com/auth/v1|' .env
sed -i 's|^SITE_URL=.*|SITE_URL=https://metrics.darknblack.com|' .env
grep -n "SUPABASE_PUBLIC_URL\|API_EXTERNAL_URL\|SITE_URL" .env
docker compose up -d
A SERVICE_ROLE_KEY nunca deve ser usada no browser.
Pode ficar em:
.env.production.local do app, se usada apenas server-side
backend interno
scripts de manutenção
Não pode ficar em:
NEXT_PUBLIC_*
frontend/browser
código commitado
logs
prints públicos
Arquivo real de produção:
/srv/homelab/apps/hub-metrics/.env.production.local
Link para Compose:
/srv/homelab/apps/hub-metrics/.env -> .env.production.local
Variáveis públicas:
NEXT_PUBLIC_*
Essas podem ir para o bundle do frontend. Não colocar secrets nelas.
Variáveis privadas:
SUPABASE_SERVICE_ROLE_KEY
CRON_SECRET
API_SECRET
WEBHOOK_SECRET
Essas só podem ser usadas no server-side.
docker exec -it hub-metrics sh
printenv | sort
exit
Não use esse comando em gravações/prints públicos porque ele pode exibir secrets.
Endpoints de cron devem exigir segredo:
Authorization: Bearer <CRON_SECRET>
ou header próprio:
x-cron-secret: <CRON_SECRET>
Nunca deixar /api/cron/* aberto sem proteção.
Antes de atualizar qualquer stack importante:
/srv/homelab/scripts/backup-homelab-daily.sh
Depois verificar:
tail -120 /srv/homelab/logs/backup-daily.log
cd /srv/homelab/apps/hub-metrics
git pull
docker compose config > /tmp/hub-metrics-app-compose-check.yml
docker compose up -d --build
docker compose ps
docker compose logs --tail=120 hub-metrics
Antes:
/srv/homelab/scripts/backup-supabase-project.sh hub-metrics
Nunca rodar comandos destrutivos sem backup:
Evitar: docker compose down -v
Evitar: rm -rf volumes/db/data
Evitar: reset.sh em stack com dados reais
docker compose down -v só deve ser usado em stack nova, descartável, sem dados reais.
Rodar 1 vez por semana:
# Espaço em disco
df -h
# Containers
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# Backups recentes
find /srv/homelab/backups/local -type f -mtime -2 -print
# Logs de backup
tail -120 /srv/homelab/logs/backup-daily.log
# Caddy válido
cd /srv/homelab/proxy && docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile
# Portas abertas no host
ss -ltnp
Checklist:
[ ] Backups rodaram nas últimas 24h
[ ] Dumps não estão vazios
[ ] Disco não passou de 80%
[ ] Nenhum container crítico está restartando
[ ] Caddy valida sem erro
[ ] Cloudflare Tunnel está online
[ ] Nenhum app publicou porta indevida no host
[ ] Nenhum .env foi commitado
Backup sem restore testado é só uma promessa.
Mensalmente:
Registro sugerido:
Data:
Projeto:
Backup usado:
Restore funcionou? Sim/Não
Tempo de restore:
Problemas encontrados:
Ações corretivas:
Salvar em:
/srv/homelab/backups/restore-tests/restore-log.md
cd /srv/homelab/apps/hub-metrics
docker compose logs --tail=120 hub-metrics
docker compose ps
git status
git log --oneline -5
Ações:
1. Reverter último commit se foi deploy ruim.
2. Restaurar .env.production.local se foi alteração de env.
3. Rebuildar container.
4. Se banco foi afetado, restaurar dump.
cd /srv/homelab/supabase/hub-metrics/docker
docker compose ps
docker compose logs --tail=120 db
docker compose logs --tail=120 auth
docker compose logs --tail=120 kong
Ações:
1. Não rodar down -v.
2. Fazer backup do estado atual.
3. Identificar serviço quebrado.
4. Restaurar .env/docker-compose se alteração de config.
5. Restaurar dump apenas se dados foram corrompidos.
Ordem:
1. Instalar Ubuntu/Docker/Git.
2. Recriar /srv/homelab.
3. Restaurar /srv/homelab/proxy.
4. Restaurar /srv/homelab/infra/cloudflared.
5. Restaurar stacks Supabase.
6. Restaurar dumps dos bancos.
7. Clonar apps pelo Git.
8. Restaurar .env.production.local dos apps.
9. Subir Caddy.
10. Subir Supabase.
11. Subir apps.
12. Validar Cloudflare Tunnel.
cd /srv/homelab/apps/hub-metrics
docker compose ps
docker compose logs --tail=120 hub-metrics
docker compose config > /tmp/hub-metrics-app-compose-check.yml
cd /srv/homelab/supabase/hub-metrics/docker
docker compose ps
docker compose logs --tail=120 kong
docker compose logs --tail=120 auth
docker compose logs --tail=120 db
curl -I http://127.0.0.1:8003
cd /srv/homelab/proxy
docker compose ps
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
docker compose logs --tail=120 caddy
cd /srv/homelab/infra/cloudflared
docker compose ps
docker compose logs --tail=120 cloudflared
Nunca rodar docker compose down -v em stack com dados reais.
Nunca apagar /srv/homelab/supabase/<projeto>/docker/volumes/db/data sem backup.
Nunca trocar POSTGRES_PASSWORD de stack existente sem plano de migração.
Nunca commitar .env.
Nunca deixar service role em NEXT_PUBLIC_*.
Nunca expor Postgres na internet.
Nunca expor Docker socket.
Nunca atualizar Supabase sem backup do banco.
Nunca confiar em backup que nunca foi restaurado.
Ao criar um novo projeto:
[ ] Criar /srv/homelab/apps/<projeto>
[ ] Criar /srv/homelab/supabase/<projeto>/docker, se precisar de Supabase próprio
[ ] Garantir portas únicas para Kong/Postgres/Pooler
[ ] Criar .env do Supabase e guardar backup criptografado
[ ] Criar .env.production.local do app e .env symlink
[ ] Garantir .env e .env.production.local no .gitignore
[ ] Adicionar app ao Caddy
[ ] Proteger admin/studio com Cloudflare Access ou manter local
[ ] Adicionar projeto ao script backup-homelab-daily.sh
[ ] Rodar backup manual inicial
[ ] Testar restore do dump inicial
trusted_proxies: https://caddyserver.com/docs/caddyfile/optionsreverse_proxy: https://caddyserver.com/docs/caddyfile/directives/reverse_proxyPara rotina diária:
/srv/homelab/scripts/backup-homelab-daily.sh
Para backup manual de um projeto:
/srv/homelab/scripts/backup-supabase-project.sh hub-metrics
Para validar Caddy:
cd /srv/homelab/proxy
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile
Para ver backups recentes:
find /srv/homelab/backups/local -type f -mtime -2 -print
Para regra mental:
Código fica no Git.
Dados ficam no Postgres/Storage.
Secrets ficam no .env e em backup criptografado.
Caddy roteia tudo.
Cloudflare Tunnel entra pelo Caddy.
Backup só vale depois de restore testado.