# Migração do stack `sistema` para a VPS 154.12.253.40 (15/11/2025) Este documento registra o procedimento completo usado para clonar e mover o stack `sistema` (Convex self-hosted + dashboard + web/Next.js) da VPS antiga `217.216.64.168` para a VPS nova `154.12.253.40` (ambas Ubuntu 24.04 com Docker Swarm). Use-o como referência para futuros blue/green ou rollbacks. ## 1. Componentes inventariados | Item | VPS antiga | VPS nova | | --- | --- | --- | | Hostname | `vmi2889318` | `vmi2872619` | | CPU/RAM | 4 vCPU / 8 GB | 8 vCPU / 24 GB | | Stack | `sistema_convex_backend`, `sistema_convex_dashboard`, `sistema_web` | os mesmos + outros stacks existentes | | Redes relevantes | `traefik_public`, `digital_network` | idem | | Volumes do stack | `sistema_convex_data`, `sistema_sistema_db`, `sistema_db` | copiados 1:1 | | Bind mount | `/apps/sistema` (5 GB) | `/home/renan/apps/sistema` + symlink `/apps/sistema` | Arquivos auxiliares recuperados da VPS antiga e copiados para `/root` na nova: - `/root/services.json` – inventário completo de serviços, útil para recriar stacks. - `/root/replay_services_from_json.sh` – script que recria serviços usando `services.json`. ## 2. Pré-requisitos e boas práticas 1. **Autenticação**: senha root na VPS antiga (`sshpass`) e chave `codex_ed25519` para a nova (chmod 600). 2. **Congelamento de escrita**: antes do delta final, escalar a 0 os serviços que escrevem (`docker service scale sistema_* =0`). 3. **Snapshots/volumes**: cópia via contêiner Alpine + tar em streaming evita salvar arquivos temporários. 4. **DNS**: prepare um alias (ex.: `vps2.esdrasrenan.com.br`) e reduza o TTL dos CNAMEs *antes* do corte para minimizar caches antigos. 5. **TLS**: copie `acme.json` entre as VPS e recarregue o Traefik para manter os certificados válidos após o corte. ## 3. Procedimento realizado ### 3.1. Inventário e auditoria ``` # CPU/memória, Swarm, stacks e volumes na VPS antiga sshpass -p '...' ssh root@217.216.64.168 'lscpu; free -h; docker info; docker stack ls; docker stack services sistema; docker volume ls' # Idem na VPS nova (confirmar recursos extras) ssh -i codex_ed25519 root@154.12.253.40 'lscpu; free -h; docker stack ls; docker service ls' ``` ### 3.2. Cópia inicial do código e volumes ``` # Código Next.js (bind mount) sshpass -p '...' ssh root@217.216.64.168 'tar czf - -C /apps sistema' | ssh -i codex_ed25519 root@154.12.253.40 'tar xzf - -C /apps' # Volumes Docker em streaming (repetir para cada volume) for vol in sistema_convex_data sistema_sistema_db sistema_db; do sshpass -p '...' ssh root@217.216.64.168 "docker run --rm -v $vol:/data alpine tar cz -C /data ." | ssh -i codex_ed25519 root@154.12.253.40 "docker volume create $vol >/dev/null; docker run --rm -i -v $vol:/data alpine tar xz -C /data" done # Ajustar caminho esperado pelo serviço web ssh root@154.12.253.40 'mkdir -p /home/renan/apps && mv /apps/sistema /home/renan/apps/ && ln -sfn /home/renan/apps/sistema /apps/sistema' ``` ### 3.3. Recriação dos serviços no Swarm da VPS nova Recriação manual (mais controle que o script) mantendo labels do Traefik: ``` # Convex backend NET=traefik_public STACK=sistema **docker service create** --name ${STACK}_convex_backend --replicas 1 --constraint 'node.role == manager' --network $NET --mount type=volume,src=sistema_convex_data,dst=/convex/data --limit-memory 12g --env CONVEX_CLOUD_ORIGIN=https://convex.esdrasrenan.com.br --env CONVEX_SITE_ORIGIN=https://convex.esdrasrenan.com.br --env FLEET_SYNC_SECRET=... --env MACHINE_PROVISIONING_SECRET=... --env MACHINE_TOKEN_TTL_MS=2592000000 --env RUST_LOG=info --label traefik.http.routers.sistema_convex.rule='Host(`convex.esdrasrenan.com.br`)' ghcr.io/get-convex/convex-backend:latest # Dashboard (mantido 0/0 até ser necessário) docker service create ... --replicas 0 ghcr.io/get-convex/convex-dashboard:latest # Web (Next.js/Bun) – notar bind mount + volume SQLite docker service create --name ${STACK}_web --replicas 1 --constraint 'node.role == manager' --network $NET --mount type=bind,src=/home/renan/apps/sistema,dst=/app --mount type=volume,src=sistema_sistema_db,dst=/app/data --limit-memory 2g --user 1000:1000 --workdir /app --env NEXT_PUBLIC_APP_URL=https://tickets.esdrasrenan.com.br --env NEXT_PUBLIC_CONVEX_URL=https://convex.esdrasrenan.com.br --env CONVEX_INTERNAL_URL=http://sistema_convex_backend:3210 --env BETTER_AUTH_SECRET=... --label traefik.http.routers.sistema_web.rule='Host(`tickets.esdrasrenan.com.br`)' oven/bun:1.3.1 bash -lc 'bash /app/scripts/start-web.sh' ``` ### 3.4. Certificados TLS 1. Copiar `acme.json` das duas VPS. 2. Mesclar certificados (para não perder a conta/chain nova) e reenviar para o volume `certificados`. 3. Escalar `traefik_traefik` para 0, gravar o arquivo e retornar a 1. ``` sshpass -p '...' ssh root@217.216.64.168 'docker run --rm -v certificados:/data alpine cat /data/acme.json' > acme-old.json ssh root@154.12.253.40 'docker run --rm -v certificados:/data alpine cat /data/acme.json' > acme-new.json python3 merge_acme.py # script que mescla Certificates cat acme-merged.json | ssh root@154.12.253.40 'docker run --rm -i -v certificados:/data alpine sh -c "cat > /data/acme.json && chmod 600 /data/acme.json"' ``` ### 3.5. Delta final antes do corte 1. `docker service scale sistema_* =0` na VPS antiga. 2. Repetir o loop de volumes + `tar` do código para pegar qualquer alteração mais recente. 3. Reaplicar `chown -R 1000:1000 /home/renan/apps/sistema` e re-symlink `/apps/sistema`. 4. Forçar rolling update nos serviços da VPS nova: ``` ssh root@154.12.253.40 'docker service update --force sistema_convex_backend sistema_web' ``` ### 3.6. DNS - Criado `vps2.esdrasrenan.com.br` (A → 154.12.253.40). - Atualizados os CNAMEs `tickets.esdrasrenan.com.br` e `convex.esdrasrenan.com.br` para apontar ao alias novo (TTL efetivo 300 s após propagação). - Observação: durante ~4 h alguns clientes ainda enxergaram o IP antigo porque o TTL prévio era 14.400 s; na próxima migração, reduzir o TTL horas antes do corte para evitar 404 temporário. ### 3.7. Imagem Bun com OpenSSL Para eliminar o warning do Prisma, foi criada uma imagem local (`sistema-web:openssl-20251115`) adicionando `apt-get install openssl`. Atualização do serviço: ``` ssh root@154.12.253.40 'docker build -t sistema-web:openssl-20251115 - <<"EOF" FROM oven/bun:1.3.1 RUN apt-get update && apt-get install -y --no-install-recommends openssl \n && rm -rf /var/lib/apt/lists/* EOF' docker service update --image sistema-web:openssl-20251115 sistema_web ``` **Atenção**: essa imagem só existe localmente; se outro nó entrar no Swarm, publique-a em um registry ou reconstrua no novo nó antes de escalar o serviço. ## 4. Verificações pós-migração - `docker service ls | grep sistema` → `convex_backend 1/1`, `web 1/1`, `dashboard 0/0`. - `curl -k -I --resolve tickets.esdrasrenan.com.br:443:154.12.253.40 https://tickets.esdrasrenan.com.br` → 307 `/login`. - `curl -k -I --resolve convex.esdrasrenan.com.br:443:154.12.253.40 https://convex.esdrasrenan.com.br` → 200. - `docker logs ` sem warnings de OpenSSL. - `dig @1.1.1.1 +short tickets.esdrasrenan.com.br` → `vps2.esdrasrenan.com.br` → `154.12.253.40`. ## 5. Recomendações para futuras trocas 1. **TTL primeiro, migração depois**: reduza CNAMEs para 300 s pelo menos 4 h antes de alterar IPs. 2. **Dashboard opcional**: mantenha `sistema_convex_dashboard` em `0/0` até precisar, escalando com `docker service scale sistema_convex_dashboard=1`. 3. **Imagem Bun custom**: mantenha o Dockerfile acima em repositório (ex.: `containers/sistema-web/Dockerfile`) para reconstruções reproduzíveis. 4. **Rollback**: para voltar à VPS antiga, restaure o DNS (CNAME → `vps.esdrasrenan.com.br`) e escale os serviços antigos para `1`. Guarde a VPS por 24–48 h antes de desativá-la definitivamente. 5. **Logs**: monitore `docker service logs sistema_convex_backend` para garantir que não haja `exit 137`. Se reaparecer, mantenha o limite de memória ≥ 12 GB ou ajuste conforme uso real. Com este fluxo documentado, repetir ou reverter a migração passa a ser um processo guiado e audível.