# Deploy em VPS (Ubuntu 24.04 / 22.04) ## Acesso à VPS de deploy Os dados de acesso da VPS onde o servidor será publicado **não estão no repositório**: ficam no próprio GitHub, em **Settings → Secrets and variables → Agents** (segredos de repositório do agente), no repositório `luizpalazzo/muonline`. - Usuário: `root`. - Senha: o segredo **`ROOT`** dessa mesma tela (Repository secrets). O endereço/IP da VPS e demais dados, se houver, ficam ali também (aba *Secrets* ou *Variables*). - Nunca copie a senha para o Git, para logs ou para a documentação (regra de segredos do projeto, AGENTS.md). Para usar: leia o segredo no GitHub e passe-o só pelo ambiente da sessão; depois do primeiro acesso, prefira chave SSH e desative o login de `root` por senha (ver *Segurança antes de abrir ao público*). ## Deploy automático (GitHub Actions) Processo completo (dia a dia, rollback, operação, problemas comuns): **[ci-cd.md](ci-cd.md)**. Workflow: [.github/workflows/deploy.yml](../.github/workflows/deploy.yml) (decisão D39). Resumo: - **Todo push e pull request** → job `validate`: `bash -n` + `shellcheck -S error` nos scripts, `docker compose config` (base, dev e prod), patches do OpenMU + compilação dos plugins `MuCustom.*` (`-p:ci=true`), patches do cliente web + `tsc --noEmit`. - **Push no `main`** (só se o `validate` passou) → job `deploy`: 1. envia o commit por `git push` via SSH para o branch `deploy` de `/opt/mu` na VPS (o repositório é privado: a VPS não precisa de credencial do GitHub; os submodules são upstreams públicos); 2. roda `scripts/deploy.sh ` na VPS → `scripts/update.sh --no-pull`: backup → build (os containers antigos continuam no ar) → up → health → smoke test; 3. se algo falhar, o `deploy.sh` volta o código ao commit anterior, refaz o build e sobe de novo (o job fica vermelho). O backup pré-deploy fica em `backups/` caso o banco precise de restore. - Um deploy por vez (`concurrency`); um push novo espera o anterior terminar. - O commit no ar fica em `/opt/mu/.deployed`. Configuração em **Settings → Secrets and variables → Actions** (não confundir com a aba *Agents*): | Nome | Tipo | Valor | |---|---|---| | `VPS_HOST` | variável | IP da VPS | | `VPS_USER` | variável ou segredo | `root` (padrão se ausente) | | `VPS_PASS` | **segredo** | senha SSH (nunca como variável: variáveis aparecem em texto puro) | Deploy manual na VPS: `cd /opt/mu && ./scripts/deploy.sh ` (o commit precisa existir lá). ## Recursos mínimos 2 vCPU, 4 GB RAM (8 GB recomendado; o build do cliente web e do OpenMU usa bastante memória), 30 GB de disco, IPv4 público. ## DNS Crie registros **A** apontando para o IP da VPS: `play`, `admin`, `ws`, `docs`, `www` e o domínio raiz (`BASE_DOMAIN`). Os certificados Let's Encrypt (desafio HTTP-01) só são emitidos depois que o DNS propagar. Sem domínio próprio: `BASE_DOMAIN=.sslip.io` (ex. `82-38-28-175.sslip.io`). O sslip.io resolve qualquer subdomínio para o IP embutido no nome, então `play.82-38-28-175.sslip.io` já funciona com HTTPS de verdade, sem configurar DNS. Para trocar depois: mude `BASE_DOMAIN` no `/opt/mu/.env` e rode `./scripts/update.sh --no-pull` (o cliente web é recompilado com o novo endereço WSS). ## Instalação ```bash ssh root@SEU_IP apt-get update && apt-get install -y git git clone --recurse-submodules /opt/mu cd /opt/mu BASE_DOMAIN=meumu.com.br ./scripts/install-vps.sh # LETSENCRYPT_EMAIL=... é opcional ``` Repositório privado sem credencial na VPS: em vez do `git clone`, crie `/opt/mu` com `git init` e envie o código com `git push ssh://root@SEU_IP/opt/mu HEAD:refs/heads/deploy`, depois `git -C /opt/mu checkout --detach deploy` (é o que o GitHub Actions faz a cada deploy). O script ([scripts/install-vps.sh](../scripts/install-vps.sh)) é idempotente e: valida root e versão do Ubuntu → instala Docker pelo repositório oficial → prepara submodules → cria `.env` (senhas aleatórias, `chmod 600`, nunca impressas) → configura UFW (SSH detectado + 80/443) → build → sobe → espera health checks → bloqueia contas de teste → mostra URLs e próximos passos. Variáveis: `BASE_DOMAIN`, `LETSENCRYPT_EMAIL`, `OPENMU_ADMIN_USER`, `OPENMU_ADMIN_PASSWORD`, `POSTGRES_PASSWORD`, `CONFIGURE_FIREWALL=yes|no`, `SSH_PORT`, `DISABLE_TEST_ACCOUNTS=yes|no`, `NONINTERACTIVE=1`. ## Portas públicas (só cliente web — D15) | Porta | Quem usa | Pública? | |---|---|---| | 22 (ou a do seu SSH) | administração | sim (só você) | | 80 | ACME (Let's Encrypt) + redirect para 443 | sim | | 443 | `play` (web), `ws` (WSS do jogo), `admin`, `docs` | sim | | 44405/44406, 55901–55906, 55980 | ConnectServers, GameServers, ChatServer | **não** — o web chega neles pelo ws-proxy, na rede interna do Docker | | 5432 | PostgreSQL | **nunca** (rede interna) | | 8080 | Admin direto | **nunca** (só via Traefik) | > O compose **não publica** nenhuma porta do jogo. > Lembre: portas publicadas pelo Docker **não passam pelo UFW** (cadeia `DOCKER` do iptables). ## Segurança antes de abrir ao público - [ ] Troque a senha inicial do painel (`painel.DOMAIN`, obrigatório no primeiro acesso). - [ ] Contas de teste bloqueadas (`scripts/disable-test-accounts.sh --list`). - [ ] Limites de conexão (D20): o script aplica `CS_MAX_CONN_PER_ADDRESS` (1000) no ConnectServer **44406** — todos os jogadores web chegam com o IP do proxy; confira com `scripts/set-connection-limit.sh --show`. O limite por jogador real é `WS_MAX_CONN_PER_IP` (20 WebSockets simultâneos por IP, no Traefik). - [x] Admin Panel do OpenMU sem rota pública (D59; `admin.DOMAIN` responde 404). Só por túnel SSH. - [x] Papéis de menor privilégio no banco (`scripts/db-roles.sh`; o deploy aplica). - [x] Backup diário pelo systemd (`scripts/backup-schedule.sh`, `--status`); conferir restores com `scripts/backup-verify.sh` (restaura num banco temporário). - [ ] Cópia **fora** da VPS (D56; código pronto e testado com remote local, **destino real ainda não existe**): 1. Crie um bucket vazio no provedor (Cloudflare R2, Backblaze B2, S3, ou um servidor SFTP) e uma chave com acesso **só** a ele. 2. Copie `infra/backup/rclone.conf.example` para `/opt/mu/.rclone.conf` (`chmod 600`) e preencha a chave, o endpoint e as duas senhas do remote `crypt` (gere com `rclone obscure`; guarde as originais fora da VPS: sem elas o backup não abre). 3. No `.env`: `RCLONE_REMOTE=mu-crypt:vps1` (opcionais: `OFFSITE_RETENTION_DAYS=30`, `OFFSITE_KEEP_MIN=7`). 4. Teste: `scripts/backup.sh && scripts/backup-offsite.sh && scripts/backup-offsite.sh --restore-test`. O timer diário (`mu-backup.service`) envia depois de cada backup; o resultado fica em `scripts/backup-offsite.sh --status`, no `healthcheck.sh` (aviso se falhou ou tem mais de 36 h) e no `vps-audit.sh`. Um remote sem `crypt` é recusado (o dump tem hashes de senha) salvo `OFFSITE_ALLOW_PLAINTEXT=1`. Teste local sem nuvem: `OFFSITE_TEST_DIR=/pasta` no `.env` e remotes `local` + `crypt` apontando para `/remote-test/...` (como em `docs/audit/revisao-1aefa26.md`). - [x] Swap de 2 GB em VPS de até 16 GB sem swap (`scripts/vps-swap.sh`, swappiness 10; o deploy cria). - [ ] `.env` com `chmod 600`, fora do Git. ## Operação ```bash cd /opt/mu ./scripts/status.sh ./scripts/healthcheck.sh ./scripts/backup.sh ./scripts/update.sh # backup → pull → rebuild → health → smoke (com instruções de rollback) ./scripts/logs.sh openmu --errors ./scripts/vps-audit.sh # retrato do host (também em Actions -> VPS ops -> audit) ./scripts/backup-verify.sh # prova de restore sem tocar o banco em uso ./scripts/load-test.sh connections 10,25,50 45 ```