Instalar Membresia 360 em uma VPS
Passo a passo completo para rodar o app em um servidor próprio (Ubuntu 22.04), com Node.js, Nginx, SSL gratuito e domínio personalizado.

Toda vez que você fizer mudanças no Lovable e sincronizar com o GitHub, rode o comando correspondente na VPS para refletir as novidades. Escolha a instância e o tipo de atualização abaixo.
Atualização normal — Porta 3001 — /var/www/membresia360-m360
cd /var/www/membresia360-m360 && export PORT=3001 && git pull && npm install && NITRO_PRESET=node-server npm run build && (pm2 restart membresia360-m360 || pm2 start .output/server/index.mjs --name membresia360-m360) && pm2 saveDica: a atualização forçada é útil quando o site continua desatualizado ou o processo aparece como errored no pm2 status.
Rode um único comando na VPS para fazer git pull, npm install, build e restart no PM2. O script carrega o .env e .env.local automaticamente.
App principal (porta 3000):
cd /var/www/membresia360 && sudo bash public/scripts/deploy.shDica: o script está em public/scripts/deploy.sh. Para outra pasta/porta, basta passar os argumentos: bash deploy.sh PASTA NOME_PM2 PORTA.
Se aparecer: deploy.sh: No such file or directory
Isso quer dizer que sua VPS ainda não recebeu o arquivo novo do GitHub. Rode este comando uma vez:
cd /var/www/membresia360 && git pull && sudo bash public/scripts/deploy.shPrefere pular o passo a passo? Rode o script abaixo na sua VPS Ubuntu recém-criada (como root). Ele instala Node.js, Bun, Nginx, PM2, faz build, sobe o app e (opcional) emite SSL grátis.
curl -fsSL https://membresia360.com.br/install-membresia360.sh -o install.sh
sudo bash install.shModo não interativo (informe repositório, domínio e e-mail):
sudo REPO_URL="https://github.com/usuario/repo.git" \
DOMAIN="app.suaigreja.com.br" \
EMAIL="admin@suaigreja.com.br" \
bash install.shO workflow .github/workflows/deploy-vps.yml já está incluso no projeto. Toda vez que você fizer push no branch main (o Lovable já faz isso quando você sincroniza com o GitHub), o GitHub Actions conecta na sua VPS via SSH e roda git pull, build e pm2 restart sozinho.
1. Gere uma chave SSH só para o deploy (na sua VPS)
ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/gh_deploy -N ""
cat ~/.ssh/gh_deploy.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
cat ~/.ssh/gh_deploy # copie TUDO — é a chave PRIVADA que vai no GitHub2. Adicione os secrets no GitHub
No repositório: Settings → Secrets and variables → Actions → New repository secret. Crie um por vez:
| Secret | Valor |
|---|---|
| VPS_HOST | IP da VPS (ex.: 91.98.120.163) |
| VPS_USER | root (ou o usuário sudo) |
| VPS_PORT | Porta SSH (padrão 22). Opcional. |
| VPS_SSH_KEY | Conteúdo do arquivo ~/.ssh/gh_deploy (privada, inclui BEGIN/END) |
| VPS_APP_PATH | Caminho do projeto (ex.: /var/www/membresia360) |
| PM2_APP_NAME | Nome do processo PM2 (ex.: membresia360) |
3. Teste
Faça qualquer push em main (ou clique em Actions → Deploy to VPS → Run workflow). Acompanhe o log em tempo real na aba Actions do GitHub. Se der verde, sua VPS já está com a versão nova.
- Use uma chave SSH exclusiva para o deploy — não reaproveite a sua pessoal.
- Nunca cole a chave privada no chat ou em commits.
- Se quiser restringir mais: crie um usuário
deployna VPS com sudo limitado só parapm2.
- 1Contratar a VPS
- 2Conectar via SSH
- 3Atualizar o sistema
- 4Instalar Node.js e Bun
- 5Instalar Nginx
- 6Criar o banco de dados no Supabase
- 7Clonar e configurar o projeto
- 8Rodar em produção com PM2
- 9Configurar domínio e DNS
- 10SSL grátis com Certbot
- 11Manutenção e atualizações
- 12Instalação automatizada com 1 clique
- 13Erros reais & sequência que funcionou
- 14Comandos de operação diária
- 15Scripts prontos & status ao vivo
- 16Configurar domínio próprio passo a passo
- 17Depois do SSL: atualizar .env e publicar
- 18Subdomínios e outros sites na mesma VPS
- 19Cloudflare: DNS, cache e proteção
- 20Vários domínios próprios no mesmo IP
- 21Instalar sem domínio, apontar depois no Registro.br
- 22Atualizar o app na porta 3001
- 23Autenticação do GitHub na VPS (PAT)
- 24Entrar em cada porta (3000 e 3001)
- 25Firewall e portas de acesso (80, 443, 3000, 3001)
- 26SSL do membresia360.com.br (porta 3001)
- 27SSL do apachedesenvolvedor.com.br (porta 3000)
- 28Atualização forçada (portas 3000 e 3001)
- 29Instalar um site novo com 1 comando
- 30Card de instalação rápida com exemplos
- 31Trocar o site de uma porta por outro repositório
- 32Pasta de backup e como restaurar
- 33Imagens não aparecem (erro 404 em /__l5e/)
- 34Painel /campanhas: erro de Supabase ou Meta
- 35Chaves do Meta — referência rápida
Marque cada comando conforme executa na VPS. Cole o output ou anexe um print como evidência. Tudo fica salvo no seu navegador.
Conectar na VPS via SSH
Confirme o login (yes) e digite a senha root do provedor.
Atualizar o sistema
Pode demorar alguns minutos. Aceite as opções padrão.
Liberar a porta 80 (parar LiteSpeed, se existir)
Só execute se aparecer erro 'bind() to 0.0.0.0:80 failed'.
Instalar Node.js 20
Instalar PM2 (gerenciador de processos)
Instalar Nginx
Confirmar Nginx escutando na porta 80
Deve mostrar 0.0.0.0:80 LISTEN nginx.
Clonar o repositório (com token PAT)
Criar arquivo .env com chaves do Supabase
Cole VITE_SUPABASE_URL, VITE_SUPABASE_PUBLISHABLE_KEY e demais variáveis.
Instalar dependências
Build de produção (preset node-server)
Sem o preset o app não sobe em Node — dá 500 no Nginx.
Subir o app com PM2 na porta 3000
Configurar Nginx como proxy reverso para :3000
proxy_pass http://127.0.0.1:3000; dentro do bloco location /.
Abrir http://SEU_IP no navegador
A landing do Membresia 360 deve carregar.
Apontar DNS (registro A) para o IP da VPS
No painel do domínio: A @ SEU_IP e A www SEU_IP.
Emitir SSL grátis com Certbot
Fluxo de atualização (novas versões)
Executa comandos autorizados diretamente na VPS onde o Membresia 360 está rodando. Requer que o servidor tenha a variável VPS_EXEC_TOKEN definida no .env. Só funciona quando esta página é acessada pelo domínio da VPS (não pelo preview do Lovable).
Clique em um botão acima para ver a saída aqui.
- VPS com Ubuntu 22.04 LTS — mínimo 1 vCPU, 2 GB de RAM, 20 GB SSD
- Acesso SSH (usuário
rootou sudo) - Domínio próprio (ex.:
minhaigreja.com.br) - Conta no Supabase com projeto criado e banco de dados configurado
- Arquivo
schema.sqldo projeto aplicado no Supabase
Contratar a VPS
Contrate uma VPS em qualquer provedor: Hostinger, Contabo, DigitalOcean, Vultr, Hetzner ou AWS Lightsail. Escolha o sistema operacional Ubuntu 22.04 LTS.
Após a criação, o painel do provedor mostrará o IP público (ex.: 203.0.113.10) e a senha root. Guarde essas informações — vamos usá-las já no próximo passo.
Conectar via SSH
Abra o terminal do seu computador (ou PuTTY no Windows) e conecte:
ssh root@SEU_IP_DA_VPS
# Ex: ssh root@203.0.113.10
# Digite a senha quando pedir.
Crie um usuário não-root (recomendado por segurança):
adduser membresia
usermod -aG sudo membresia
su - membresiaAtualizar o sistema
Atualize os pacotes do Ubuntu antes de instalar qualquer coisa:
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl git build-essential ufwAtive o firewall liberando SSH, HTTP e HTTPS:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enableInstalar Node.js e Bun
Instale o Node.js 20 LTS via NodeSource:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node -v # deve mostrar v20.x
npm -vInstale o Bun (usado pelo projeto):
curl -fsSL https://bun.sh/install | bash
source ~/.bashrc
bun --versionInstalar Nginx
O Nginx vai servir como proxy reverso do app:
sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginxAbra http://SEU_IP no navegador — deve aparecer a página "Welcome to nginx".

Criar o banco de dados no Supabase
O Membresia 360 usa o Supabase como banco de dados (PostgreSQL) e autenticação. Antes de subir o app, você precisa criar o projeto e aplicar as tabelas do sistema.
1. Crie um projeto gratuito em https://supabase.com:
- Clique em New project e escolha uma organização.
- Dê um nome (ex.:
membresia360-suaigreja). - Escolha a região mais próxima do Brasil (recomendado: South America).
- Defina uma senha segura para o banco e aguarde a criação.
2. Acesse Project Settings → API e copie os valores:
Project URL(ex.: https://abc123.supabase.co)Project API keys → anon / public(chave pública)Project API keys → service_role(chave secreta — server side)
3. Aplique o schema do banco de dados:
- Abra SQL Editor → New query.
- Cole o conteúdo do arquivo
schema.sqlque acompanha o projeto. - Clique em Run. Isso cria as tabelas, políticas (RLS), funções e triggers.
-- Exemplo rápido: confirme se as tabelas foram criadas
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public';4. Configure o Auth do Supabase (opcional, mas recomendado):
- Vá em Authentication → Settings e ajuste o site URL para o seu domínio.
- Desative provedores que não usar (Google, GitHub, etc.) para manter a segurança.
- Em Email Templates personalize os e-mails de confirmação e recuperação.
Importante: a chave service_role nunca deve ir para o frontend. Ela é usada apenas no servidor (.env local/VPS) para funções administrativas.
Clonar e configurar o projeto
Clone o projeto na VPS (troque pela URL do seu repositório):
cd ~
git clone https://github.com/SEU_USUARIO/membresia-360.git app
cd app
bun installRepositório privado no GitHub?
O GitHub não aceita senha diretamente no git clone. Se seu repositório for privado, crie um Personal Access Token (PAT):
- Acesse https://github.com/settings/tokens
- Clique em Generate new token (classic)
- Marque a permissão repo e copie o token
Use o token no lugar da senha no comando abaixo:
git clone https://SEU_TOKEN@github.com/SEU_USUARIO/membresia-360.git appDica: se o repo não tiver dados sensíveis, você pode torná-lo público em Settings → Visibility → Public.
Crie o arquivo .env na raiz do projeto com as credenciais do Supabase:
nano .env# Frontend (anon/public key)
VITE_SUPABASE_URL=https://SEU_PROJETO.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
VITE_SUPABASE_PUBLISHABLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
VITE_SUPABASE_PROJECT_ID=SEU_PROJETO
# Backend (service_role key — nunca exponha no navegador)
SUPABASE_URL=https://SEU_PROJETO.supabase.co
SUPABASE_PUBLISHABLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Opcional: URL pública do site (usada em links/QR Code)
VITE_PUBLIC_SITE_URL=https://app.suaigreja.com.brSubstitua SEU_PROJETO pelo ID do projeto Supabase e as chaves pelos valores copiados no passo anterior.
Salve (Ctrl+O, Enter, Ctrl+X) e gere o build de produção para VPS:
NITRO_PRESET=node-server bun run build:vpsRodar em produção com PM2
PM2 mantém o app rodando 24/7 e reinicia em caso de erro:
sudo npm install -g pm2
PORT=3000 NODE_ENV=production pm2 start "bun run start" --name membresia
pm2 save
pm2 startup systemd # copie e cole o comando que aparecerAgora configure o Nginx como proxy do app (porta 3000 por padrão):
sudo nano /etc/nginx/sites-available/membresiaserver {
listen 80;
server_name minhaigreja.com.br www.minhaigreja.com.br;
location / {
proxy_pass http://127.0.0.1:3000;
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;
proxy_cache_bypass $http_upgrade;
}
}Ative o site e recarregue o Nginx:
sudo ln -s /etc/nginx/sites-available/membresia /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxConfigurar domínio e DNS
No painel do seu registrador (Registro.br, GoDaddy, etc.), crie os registros:
Tipo Nome Valor TTL
A @ IP_DA_SUA_VPS 3600
A www IP_DA_SUA_VPS 3600Aguarde a propagação (10 minutos a 24 horas). Teste com:
ping minhaigreja.com.br
SSL grátis com Certbot
Instale o Let's Encrypt via Certbot para ativar HTTPS:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d minhaigreja.com.br -d www.minhaigreja.com.brAceite os termos, forneça um e-mail e escolha a opção de redirecionar HTTP → HTTPS. O Certbot renova o certificado sozinho a cada 60 dias.

Manutenção e atualizações
Para publicar uma nova versão do app:
cd ~/app
git pull
bun install
NITRO_PRESET=node-server bun run build:vps
pm2 restart membresiaComandos úteis do PM2:
pm2 status # ver processos
pm2 logs membresia # ver logs em tempo real
pm2 restart membresia # reiniciar
pm2 stop membresia # pararPronto! Seu Membresia 360 está rodando em uma VPS própria, com domínio e SSL. Em caso de dúvidas, fale com o suporte via WhatsApp (69) 99278-4711.
Instalação automatizada com 1 clique
Se você preferir automatizar todo o processo acima, use o script oficialinstall-membresia360.sh. Ele instala Node.js, Bun, Nginx, PM2, clona o repositório, faz o build, sobe o app e ainda emite SSL grátis via Certbot (se informar domínio e e-mail).
Observação: o script automatiza a parte da VPS, mas ainda é necessário ter um projeto Supabase criado e as chaves configuradas no arquivo .env antes de rodar o script (veja o Passo 6).
1. Baixe o script na VPS:
curl -fsSL https://membresia360.com.br/install-membresia360.sh -o install.sh2. Rode como root:
sudo bash install.shModo não interativo (responde tudo por variáveis de ambiente):
sudo REPO_URL="https://github.com/usuario/repo.git" \
DOMAIN="app.suaigreja.com.br" \
EMAIL="admin@suaigreja.com.br" \
VITE_SUPABASE_URL="https://SEU_PROJETO.supabase.co" \
VITE_SUPABASE_ANON_KEY="sua_chave_anon" \
SUPABASE_SERVICE_ROLE_KEY="sua_chave_service_role" \
bash install.shO que o script faz por você:
- Atualiza pacotes e ativa firewall UFW
- Instala Node.js 20 LTS e Bun
- Instala Nginx e PM2
- Clona ou atualiza o repositório do app
- Valida se as variáveis do Supabase foram preenchidas antes de prosseguir (VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY e SUPABASE_SERVICE_ROLE_KEY)
- Instala dependências e gera build de produção
- Configura proxy reverso no Nginx
- Emite certificado SSL gratuito com Certbot (opcional)
Erros reais & sequência que funcionou
Esta seção documenta os erros reais encontrados durante uma instalação de produção (VPS Hetzner Ubuntu, IP 91.98.120.163) e a sequência exata de comandos que resolveu cada um. Use como referência rápida quando algo travar.
Erro 1 — Nginx não sobe na porta 80 (LiteSpeed ocupando)
Mensagem: nginx: bind() to 0.0.0.0:80 failed (98: Address already in use). Acontece em VPS que já vêm com OpenLiteSpeed / lsws pré-instalado (Hostinger, alguns templates da Hetzner).
# Descobrir quem está usando a porta 80
sudo ss -tlnp | grep ':80 '
# Parar e desabilitar o LiteSpeed
sudo systemctl stop lsws || sudo /usr/local/lsws/bin/lswsctrl stop
sudo systemctl disable lsws 2>/dev/null || true
# Confirmar que a porta 80 está livre e subir o Nginx
sudo systemctl start nginx
sudo ss -tlnp | grep nginx # deve mostrar nginx escutando em :80Erro 2 — E: Unable to locate package upgrade
O comando certo é apt upgrade (verbo), não apt install upgrade.
# ERRADO
sudo apt install upgrade
# CERTO
sudo apt update && sudo apt upgrade -yErro 3 — install.sh: No such file or directory
Você tentou rodar o script antes de baixá-lo, ou baixou em outra pasta. Sempre rode os dois comandos na mesma sessão e na mesma pasta:
cd /root
curl -fsSL https://membresia360.com.br/install-membresia360.sh -o install.sh
ls -la install.sh # confirme que existe
sudo bash install.shErro 4 — Repository not found no git clone
Repositório privado exige Personal Access Token (PAT) e a URL correta do repositório. Confira o dono do repo no GitHub — não use neuzinaldo/… se o repo está em jogosdaunidade/…, por exemplo.
# Formato com token embutido (repo privado)
git clone https://SEU_PAT@github.com/DONO_REAL/NOME_DO_REPO.git /var/www/igreja-conectada-360
# Depois de clonar, REVOGUE o token em:
# https://github.com/settings/tokensSegurança: nunca deixe um PAT em histórico de shell público. Revogue e gere um novo assim que terminar o clone.
Erro 5 — ERR_MODULE_NOT_FOUND: dist/server/server.js
Aconteceu ao tentar usar vite preview como servidor de produção. TanStack Start não é um SPA estático — ele precisa do bundle SSR do Nitro. A correção é gerar o build com preset node-server e subir o .output/server/index.mjs.
Erro 6 — 500 Internal Server Error do Nginx
Ocorre quando o Nginx aponta para uma pasta estática que não existe (dist/client/index.html) ou faz proxy para uma porta onde nada está escutando. A solução definitiva é servir via proxy reverso para o processo Node do Nitro na porta 3000.
Sequência final que funcionou (copiar e colar na VPS)
Rode como root, dentro da pasta do projeto já clonado (/var/www/igreja-conectada-360) e com o .env preenchido:
cd /var/www/igreja-conectada-360
# 1. Limpar processos e builds antigos
pm2 delete igreja-conectada-360 || true
rm -rf dist .output
# 2. Instalar dependências e gerar build SSR para VPS (Node)
npm install
NITRO_PRESET=node-server npm run build
# 3. Confirmar que o bundle do servidor foi gerado
ls -la .output/server/index.mjs
# 4. Subir com PM2 na porta 3000
PORT=3000 NODE_ENV=production pm2 start "node .output/server/index.mjs" \
--name igreja-conectada-360 --update-env
pm2 save
pm2 startup systemd -u root --hp /root | tail -n 1 | bash || true
# 5. Configurar Nginx como proxy reverso para :3000
sudo tee /etc/nginx/sites-available/igreja-conectada-360 >/dev/null <<'EOF'
server {
listen 80;
server_name _;
client_max_body_size 25M;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
EOF
sudo ln -sf /etc/nginx/sites-available/igreja-conectada-360 /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx
# 6. Testar
curl -I http://127.0.0.1:3000 # deve retornar HTTP/1.1 200
curl -I http://SEU_IP_DA_VPS # também deve retornar 200Diagnóstico rápido quando algo falhar
# Nginx com erro?
sudo tail -n 80 /var/log/nginx/error.log
# App caiu?
pm2 status
pm2 logs igreja-conectada-360 --lines 100
# Porta 80 ocupada?
sudo ss -tlnp | grep ':80 '
# Testar se o Node está respondendo antes do Nginx
curl -v http://127.0.0.1:3000Publicando atualizações depois
A VPS não atualiza sozinha quando você mexe no Lovable. Rode este bloco sempre que quiser puxar as mudanças:
cd /var/www/igreja-conectada-360
git pull --ff-only
npm install
NITRO_PRESET=node-server npm run build
pm2 restart igreja-conectada-360Comandos de operação diária
Guia rápido com os comandos que você mais vai usar depois que a VPS já está no ar. Todos os caminhos assumem a instalação padrão em /var/www/membresia360.
Fluxo para atualizar o site depois de mudanças no Lovable
- Edite o que precisar no Lovable.
- Publique (ou faça sync/push) para enviar o código ao GitHub na branch
main. - Na VPS, dentro de
/var/www/membresia360, rode o comando de deploy abaixo. - Abra o navegador no domínio e aperte Ctrl+F5 para limpar o cache.
O domínio www.membresia360.com.br aponta para a porta 3000. Sempre atualize a pasta /var/www/membresia360.
Deploy completo com 1 comando (git pull + build + restart)
Use o script public/scripts/deploy.sh para atualizar a VPS inteira de uma vez. Ele carrega .env e .env.local, faz pull, instala dependências, builda e reinicia o PM2.
cd /var/www/membresia360 && sudo bash public/scripts/deploy.shPara a segunda instância (porta 3001):
cd /var/www/membresia360 && sudo bash public/scripts/deploy.sh /var/www/membresia360-m360 membresia360-m360 3001Erro comum: deploy.sh: No such file or directory
Acontece quando a VPS ainda está com o código antigo, sem o script novo. Primeiro puxe o GitHub e depois rode o deploy:
cd /var/www/membresia360 && git pull && sudo bash public/scripts/deploy.shReiniciar só o app (mais comum)
Se o processo ainda não existir no PM2 (ex.: instalação nova ou após pm2 delete), o restart falha. O comando abaixo cai automaticamente no start nesse caso.
pm2 restart membresia360 || pm2 start /var/www/membresia360/.output/server/index.mjs --name membresia360
pm2 saveReiniciar a VPS inteira
O site fica fora do ar alguns minutos até o sistema voltar.
sudo reboot
# ou
sudo shutdown -r nowReiniciar o Nginx
sudo systemctl restart nginx
# ou só recarregar configurações
sudo systemctl reload nginxVer status de tudo
pm2 status
systemctl status nginx --no-pager
sudo ss -tlnp | grep -E 'nginx|:3000|:3001'Por que o domínio não aparece no PM2?
O PM2 gerencia os processos Node, não os domínios. Por isso você vê nomes como membresia360 e membresia360-m360, mas não vê apachedesenvolvedor.com.br. Os domínios ficam no Nginx, que faz o proxy reverso para as portas 3000/3001.
Para ver qual domínio aponta para qual configuração, liste os sites do Nginx:
ls -la /etc/nginx/sites-enabled/
# ou, para ver os nomes de servidor de cada config:
awk '/server_name/{print FILENAME ": " $0}' /etc/nginx/sites-enabled/*Ver logs
# Logs do app
pm2 logs membresia360 --lines 100
# Logs do Nginx
sudo tail -n 80 /var/log/nginx/error.log
sudo tail -n 80 /var/log/nginx/access.logAtualizar o app depois de mudanças no Lovable
cd /var/www/membresia360
git pull
npm install
NITRO_PRESET=node-server npm run build
pm2 restart membresia360 || pm2 start /var/www/membresia360/.output/server/index.mjs --name membresia360
pm2 saveAtualização forçada (limpeza profunda)
Use quando o site continua mostrando a versão antiga.
cd /var/www/membresia360 && git fetch origin && git reset --hard origin/main && rm -rf .output node_modules/.vite dist && npm install && NITRO_PRESET=node-server npm run build && (pm2 restart membresia360 || pm2 start /var/www/membresia360/.output/server/index.mjs --name membresia360) && pm2 saveCorrigir erro 502 Bad Gateway
Significa que o Nginx está de pé, mas o app Node caiu.
pm2 delete membresia360 || true && cd /var/www/membresia360 && NITRO_PRESET=node-server npm run build && pm2 start ".output/server/index.mjs" --name membresia360 && pm2 save && systemctl reload nginxApagar processos antigos/errored do PM2
Troque NOME_DO_PROCESSO pelo nome que aparece na coluna name do pm2 status (ex.: igreja-conectada-360).
pm2 delete NOME_DO_PROCESSO
pm2 saveVer quem está usando a porta 80
sudo ss -tlnp | grep ':80 '
# Se aparecer lsws (LiteSpeed), pare ele:
sudo systemctl stop lsws || sudo /usr/local/lsws/bin/lswsctrl stop
sudo systemctl disable lsws 2>/dev/null || true
sudo systemctl start nginxAbrir, fechar e ver portas no firewall (UFW)
O Ubuntu usa o ufw como firewall padrão. Você precisa abrir a porta do app enquanto testa por IP, e depois fechá-la quando o Nginx/SSL estiver no ar.
# Ver regras atuais
sudo ufw status
# Abrir uma porta (ex.: 3000) para testar pelo IP:3000
sudo ufw allow 3000/tcp
sudo ufw reload
# Fechar a porta depois de configurar o domínio
sudo ufw delete allow 3000/tcp
sudo ufw reload
# Permitir apenas Nginx (80 e 443) e SSH (22) — padrão seguro
sudo ufw allow 'Nginx Full'
sudo ufw allow OpenSSH
sudo ufw reloadCuidado: se você estiver acessando a VPS por SSH, não feche a porta 22 (OpenSSH) antes de ter outro acesso configurado. Sem SSH, você pode ficar trancado fora do servidor.
Atualizar pacotes do sistema
sudo apt update && sudo apt upgrade -yParar ou iniciar o app manualmente
pm2 stop membresia360 # para
pm2 start membresia360 # inicia
pm2 delete membresia360 # remove do PM2Quando o comando de atualização "não faz nada"
Se você rodou o comando de atualização e o site continuou igual, rode esses comandos um por um dentro da pasta do projeto. Eles mostram onde a atualização travou: pasta errada, conflito no Git, build falhando ou processo parado no PM2.
cd /var/www/membresia360 && pwd
git status
git log --oneline -5
git pull
pm2 statusLeia a saída de cada um. Se git status mostrar conflitos, resolva ou use a atualização forçada abaixo. Se pm2 status mostrar o processo em errored, remova com pm2 delete nome-do-processo.
Atualização forçada (quando a normal não funciona)
Ignora possíveis conflitos no Git, limpa o cache de build e reinstala tudo. Use só quando a atualização normal falhar.
cd /var/www/membresia360 && git fetch origin && git reset --hard origin/main && rm -rf .output node_modules/.vite dist && npm install && NITRO_PRESET=node-server npm run build && (pm2 restart membresia360 || pm2 start /var/www/membresia360/.output/server/index.mjs --name membresia360) && pm2 saveDica final
Sempre que atualizar e o navegador parecer não refletir as mudanças, aperte Ctrl+F5 para limpar o cache. A maior parte dos "site antigo" é só cache.
Scripts prontos & status ao vivo
Baixe os scripts .sh, envie para sua VPS (por SFTP ou wget) e execute com bash nome-do-script.sh. Todos assumem a instalação padrão em /var/www/membresia360 — para outra pasta, exporte APP_DIR antes de rodar.
Baixar todos de uma vez direto na VPS
Substitua SEU-DOMINIO pelo seu domínio (ou use o IP da VPS):
mkdir -p ~/scripts && cd ~/scripts
for f in restart-app.sh restart-nginx.sh reboot-vps.sh status.sh update.sh update-force.sh fix-502.sh; do
wget -q "https://SEU-DOMINIO/scripts/$f" -O "$f"
done
chmod +x *.sh
ls -laPainel de status ao vivo
Mostra em tempo real se o PM2 e o Nginx estão online, uso de CPU/RAM, restarts, uptime, portas abertas e espaço em disco. Requer que a VPS esteja com o VPS_EXEC_TOKEN configurado no .env (mesmo token do painel de execução abaixo).
Vazio = usa a URL atual da página.
Verifica se o HTML está sendo servido do cache do navegador, CDN ou Service Worker.
Requer que o endpoint /api/public/vps-status esteja rodando na VPS e a variável VPS_EXEC_TOKEN configurada no .env.
Configurar domínio próprio passo a passo
Este passo mostra a sequência exata para apontar um domínio já comprado (ex.: apachedesenvolvedor.com.br) para a sua VPS e ativar HTTPS. Substitua SEU-DOMINIO.COM.BR e IP_DA_VPS pelos seus dados reais.
Exemplo real: domínio apachedesenvolvedor.com.br apontado para o IP 91.98.120.163.
1. Apontar o DNS no painel do registrador
Acesse o painel de onde você comprou o domínio (Registro.br, Hostinger, GoDaddy, etc.) e crie estes registros:
Tipo Nome Valor TTL
A @ IP_DA_VPS 3600
A www IP_DA_VPS 3600Para o exemplo apachedesenvolvedor.com.br com IP 91.98.120.163:
Tipo Nome Valor TTL
A @ 91.98.120.163 3600
A www 91.98.120.163 36001.1 Passo a passo visual no Registro.br
- Acesse https://registro.br e faça login em Minha conta.
- No menu lateral, clique em Domínios e depois em apachedesenvolvedor.com.br.
- Clique na aba Editar zona (ou Zona DNS / Gerenciar DNS).
- Clique em Novo registro e adicione duas entradas do tipo
A:- Nome
@(ou deixe em branco) → Valor91.98.120.163→ TTL3600 - Nome
www→ Valor91.98.120.163→ TTL3600
- Nome
- Clique em Salvar ou Aplicar alterações.
- Aguarde a propagação (geralmente poucos minutos, máximo 72 horas).

Dica: na Registro.br o campo @ significa o domínio raiz. Se o painel não aceitar @, deixe o campo "Nome" em branco.
Aguarde a propagação. Pode ser de 10 minutos até 72 horas, mas geralmente funciona em poucos minutos. Teste com:
ping SEU-DOMINIO.COM.BR
# ou, no exemplo:
ping apachedesenvolvedor.com.br2. Ajustar o Nginx para responder pelo domínio
Conecte-se na VPS por SSH e rode os comandos abaixo. Eles criam o arquivo de configuração do Nginx apontando para o app na porta 3000.
cd /var/www/membresia360
sudo tee /etc/nginx/sites-available/membresia360 >/dev/null <<'EOF'
server {
listen 80;
server_name SEU-DOMINIO.COM.BR www.SEU-DOMINIO.COM.BR;
client_max_body_size 25M;
location / {
proxy_pass http://127.0.0.1:3000;
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;
proxy_cache_bypass $http_upgrade;
}
}
EOF
# Exemplo para apachedesenvolvedor.com.br:
# sudo tee /etc/nginx/sites-available/membresia360 >/dev/null <<'EOF'
# server {
# listen 80;
# server_name apachedesenvolvedor.com.br www.apachedesenvolvedor.com.br;
# client_max_body_size 25M;
# ...
# }
# EOF
sudo ln -sf /etc/nginx/sites-available/membresia360 /etc/nginx/sites-enabled/membresia360
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx3. Emitir certificado SSL grátis (HTTPS)
Instale o Certbot e peça o certificado para o domínio e o www. Ele já configura o redirecionamento HTTP → HTTPS automaticamente.
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d SEU-DOMINIO.COM.BR -d www.SEU-DOMINIO.COM.BR --non-interactive --agree-tos -m admin@SEU-DOMINIO.COM.BR --redirectPara o exemplo apachedesenvolvedor.com.br:
sudo certbot --nginx -d apachedesenvolvedor.com.br -d www.apachedesenvolvedor.com.br --non-interactive --agree-tos -m admin@apachedesenvolvedor.com.br --redirectO Certbot renova o certificado sozinho. Para testar a renovação automática:
sudo certbot renew --dry-run3.1 SSL automatizado (aguarda DNS + redirect HTTPS)
Script que espera o DNS propagar (checa a cada 30s por até 30 min) e, assim que @ e www apontarem para o IP da VPS, emite o certificado e ativa o redirect HTTP → HTTPS automaticamente.
curl -fsSL https://apachedesenvolvedor.com.br/scripts/ssl-auto.sh -o /tmp/ssl-auto.sh
sudo bash /tmp/ssl-auto.sh SEU-DOMINIO.COM.BR admin@SEU-DOMINIO.COM.BR
# Exemplo real para apachedesenvolvedor.com.br:
sudo bash /tmp/ssl-auto.sh apachedesenvolvedor.com.br neuzinaldo@gmail.comO script instala o Certbot se faltar, valida propagação via dig @8.8.8.8, roda certbot --nginx --redirect e finaliza com um --dry-run.
3.2 Renovação automática (systemd timer + dry-run)
Garante que o timer certbot.timer esteja ativo (roda 2x/dia) e, se por algum motivo não existir, cria um cron diário de fallback. Ao final, executa um dry-run para provar que o certificado vai se renovar sozinho antes de expirar.
curl -fsSL https://apachedesenvolvedor.com.br/scripts/ssl-renew-setup.sh -o /tmp/ssl-renew.sh
sudo bash /tmp/ssl-renew.sh
# Ver quando será a próxima renovação:
systemctl list-timers | grep certbot
# Forçar um teste de renovação a qualquer momento:
sudo certbot renew --dry-runDica: o Let's Encrypt emite certificados de 90 dias. Com o timer ativo, o Certbot tenta renovar automaticamente a partir dos 30 dias antes do vencimento — você não precisa fazer nada.
4. Verificar se tudo está no ar
pm2 status
pm2 logs membresia360 --lines 50
# Teste local (deve retornar HTTP/1.1 200):
curl -I http://127.0.0.1:3000
# Teste pelo domínio (depois da propagação DNS):
curl -I https://SEU-DOMINIO.COM.BR
# ou
# curl -I https://apachedesenvolvedor.com.brAcesse no navegador: https://SEU-DOMINIO.COM.BR(ex.: https://apachedesenvolvedor.com.br).
Problemas comuns ao configurar o domínio
Nginx não sobe na porta 80: outro serviço (OpenLiteSpeed/lsws) pode estar ocupando a porta. Pare ele e suba o Nginx:
sudo ss -tlnp | grep ':80 '
sudo systemctl stop lsws || sudo /usr/local/lsws/bin/lswsctrl stop
sudo systemctl disable lsws 2>/dev/null || true
sudo systemctl start nginxCertbot não consegue validar: o DNS ainda não propagou. Confira com:
nslookup SEU-DOMINIO.COM.BR
# Deve retornar o IP da VPS (ex.: 91.98.120.163)Site abre pelo IP mas não pelo domínio: aguarde mais um pouco a propagação DNS ou verifique se o registro A está correto no painel do registrador.
Comando rápido para reaplicar Nginx + SSL
Se você trocar de domínio futuramente, rode este bloco inteiro (substitua os valores pelo novo domínio/e-mail):
DOMAIN="SEU-DOMINIO.COM.BR"
EMAIL="admin@$DOMAIN"
cd /var/www/membresia360
# Recria config do Nginx
sudo tee /etc/nginx/sites-available/membresia360 >/dev/null <<EOF
server {
listen 80;
server_name $DOMAIN www.$DOMAIN;
client_max_body_size 25M;
location / {
proxy_pass http://127.0.0.1:3000;
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;
proxy_cache_bypass $http_upgrade;
}
}
EOF
sudo ln -sf /etc/nginx/sites-available/membresia360 /etc/nginx/sites-enabled/membresia360
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
# Emite SSL
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d "$DOMAIN" -d "www.$DOMAIN" --non-interactive --agree-tos -m "$EMAIL" --redirectLembrete: depois de qualquer mudança no Nginx, rode sudo nginx -t antes de sudo systemctl reload nginx para evitar quebrar o site.
Depois do SSL: atualizar .env e publicar
O Certbot já emitiu o certificado e o Nginx já redireciona HTTP → HTTPS. Agora você precisa contar ao próprio app qual é o domínio oficial dele, senão metadados, links e compartilhamentos ainda vão sair com a URL antiga.
1. Atualizar o .env na VPS
O arquivo .env na VPS pode ser diferente do que está no GitHub. Edite-o diretamente no servidor — não commite chaves e URLs sensíveis no repositório.
cd /var/www/membresia360
nano .envLocalize (ou adicione) a linha:
VITE_PUBLIC_SITE_URL=https://apachedesenvolvedor.com.br⚠️ Atenção: substitua apachedesenvolvedor.com.br pelo seu domínio real. Se estiver com http://, troque para https://.
2. Salvar, recompilar e reiniciar
No Nano, aperte Ctrl+O, depois Enter para salvar, e Ctrl+X para sair. Em seguida, rode:
cd /var/www/membresia360
NITRO_PRESET=node-server npm run build
pm2 restart membresia360
pm2 saveO build pode levar de 30s a 2min na primeira vez. O PM2 já reinicia o app sem derrubar o Nginx na frente.
3. Confirmar que o site está no ar
No navegador, abra:
https://apachedesenvolvedor.com.brPara conferir se o certificado e o redirect estão perfeitos, use a página de diagnóstico do próprio app:
https://apachedesenvolvedor.com.br/ssl-statusDigite o domínio e clique em Verificar. Você deve ver HTTPS respondendo, redirect ativo e dias restantes do certificado.
4. Comando rápido para reaplicar tudo
Se precisar repetir no futuro (trocou de domínio, por exemplo), este é o bloco completo:
cd /var/www/membresia360
# 1. edite .env: VITE_PUBLIC_SITE_URL=https://SEU-DOMINIO.COM.BR
# 2. salve e saia do editor
NITRO_PRESET=node-server npm run build
pm2 restart membresia360
pm2 save
# 3. teste em https://SEU-DOMINIO.COM.BR/ssl-statuspm2 logs membresia360 --lines 100.Subdomínios e outros sites na mesma VPS
Você pode hospedar vários sites no mesmo servidor usando subdomínios. Cada subdomínio aponta para uma porta local diferente no Nginx, então o app principal continua na porta 3000 e os novos apps podem rodar nas portas 3001, 3002, etc.
Exemplo prático: criar o site de cifras no subdomínio cifras.apachedesenvolvedor.com.br, rodando na porta 3001.
Checklist antes de começar
- Defina o nome do novo site (ex.: cifras, blog, loja, eventos).
- Escolha uma porta livre (evite 3000; use 3001, 3002, 3003...).
- Tenha o repositório Git do novo site pronto.
- Confirme que o domínio principal já aponta para a VPS (Passo 9/16).
1. Criar o registro DNS do subdomínio
No painel do Registro.br (ou do seu registrador), adicione uma entrada do tipo A para o subdomínio, apontando para o mesmo IP da VPS:
Tipo: A
Nome: cifras
Valor: 91.98.120.163Se quiser também o www.cifras..., adicione outra entrada A com nome www.cifras.
2. Instalar o segundo app na VPS
O script abaixo clona um repositório, instala dependências, faz build e sobe o app no PM2 com uma porta exclusiva. Rode dentro da VPS:
cd /var/www/membresia360 && sudo bash public/scripts/setup-second-site.sh https://github.com/jogosdaunidade/cifras-igreja.git /var/www/cifras 3001 cifras⚠️ Ajuste os valores: troque o repositório, a pasta, a porta e o nome do processo PM2 conforme o seu projeto. A porta deve ser diferente de 3000.
3. Criar o subdomínio no Nginx e emitir SSL
Com o app rodando na porta 3001, configure o Nginx para ouvir o subdomínio e encaminhar para essa porta. O script já faz isso e emite o SSL:
cd /var/www/membresia360 && sudo bash public/scripts/add-subdomain.sh cifras.apachedesenvolvedor.com.br 3001 neuzinaldo@gmail.comO script cria o arquivo de configuração em /etc/nginx/sites-available/cifras.apachedesenvolvedor.com.br, ativa no Nginx, emite o certificado SSL e aplica o redirect HTTP → HTTPS.
4. Testar o novo site
Aguarde a propagação do DNS (pode levar alguns minutos) e abra:
https://cifras.apachedesenvolvedor.com.brVerifique se o PM2 está rodando com pm2 status e se o Nginx não tem erros de configuração com sudo nginx -t.
5. Manutenção futura
Para atualizar o segundo site, rode os comandos dentro da pasta dele (não dentro de /var/www/membresia360):
cd /var/www/cifras
git pull
npm install
NITRO_PRESET=node-server npm run build
pm2 restart cifras
pm2 save6. Gerenciar sites na mesma VPS
Para ver todos os sites, subdomínios e processos ativos em um só lugar:
cd /var/www/membresia360 && sudo bash public/scripts/list-sites.shPara remover um subdomínio (e seus certificados SSL):
cd /var/www/membresia360 && sudo bash public/scripts/remove-subdomain.sh cifras.apachedesenvolvedor.com.br| Site | Subdomínio | Porta | PM2 |
|---|---|---|---|
| Membresia 360 | apachedesenvolvedor.com.br | 3000 | membresia360 |
| Cifras | cifras.apachedesenvolvedor.com.br | 3001 | cifras |
| Blog | blog.apachedesenvolvedor.com.br | 3002 | blog |
Cloudflare: DNS, cache e proteção
A Cloudflare fica na frente da sua VPS: cuida do DNS, acelera o site com CDN mundial, esconde o IP real do servidor e bloqueia ataques (DDoS, bots). É grátis no plano básico.
Antes de começar, tenha em mãos
- O domínio já registrado (ex.:
apachedesenvolvedor.com.br). - Login do Registro.br para trocar os nameservers.
- IP da sua VPS (ex.:
91.98.120.163). - SSL do Certbot já ativo na VPS (Passo 16).
- ~20 minutos para propagar o DNS.
Criar conta grátis na Cloudflare
- Abra dash.cloudflare.com/sign-up.
- Cadastre e-mail + senha, confirme o e-mail.
- No painel, clique em Add a Site / Adicionar Site.
✅ Checagem: você caiu no painel principal com um botão azul + Add site.
Adicionar o domínio e escolher plano Free
- Digite somente o domínio raiz, sem
wwwe semhttps://— ex.:apachedesenvolvedor.com.br. - Clique em Continue.
- Na lista de planos, role até o fim e clique em Free · $0/month.
- Clique em Continue novamente.
✅ Checagem: a Cloudflare vai escanear os DNS e mostrar uma lista de registros já existentes.
Conferir / adicionar os registros DNS
Verifique se existem estes registros. Se faltar algum, clique em Add record e crie:
| Tipo | Nome | Conteúdo (IPv4) | Proxy | TTL |
|---|---|---|---|---|
| A | @ | 91.98.120.163 | 🟠 Proxied | Auto |
| A | www | 91.98.120.163 | 🟠 Proxied | Auto |
| A | cifras (opcional) | 91.98.120.163 | 🟠 Proxied | Auto |
🟠 Proxied (laranja): passa pela Cloudflare (cache + proteção).
⚪ DNS only (cinza): só resolve o IP. Use cinza em registros de e-mail (mail, MX).
Depois clique em Continue.
Trocar os nameservers no Registro.br (passo mais importante)
A Cloudflare vai mostrar 2 nameservers pessoais (parecidos com ana.ns.cloudflare.com e rick.ns.cloudflare.com). Copie os dois exatamente como aparecem.
- Abra registro.br e faça login.
- Vá em Meus Domínios → clique no seu domínio → aba DNS.
- Escolha a opção Configurar DNS → Usar outros servidores DNS.
- Apague os servidores atuais (
a.dns.br,b.dns.br) e cole os dois da Cloudflare. - Salve.
- Volte na Cloudflare e clique em Done, check nameservers.
⏱️ Propagação: 15 min a 24 h. A Cloudflare manda um e-mail dizendo "Great news! Cloudflare is now protecting your site" quando o domínio ficar Active.
🔎 Checagem manual: abra whatsmydns.net, digite seu domínio e escolha NS. Deve retornar nomes com .cloudflare.com.
Configurar SSL como Full (strict)
- No painel do domínio, menu lateral: SSL/TLS → Overview.
- Marque Full (strict).
❌ Nunca use Flexible — causa loop de redirect com o Nginx da VPS.
✅ Só use Full (strict) depois do Certbot rodar OK (Passo 16).
Ativar HTTPS obrigatório e otimizações
Ainda em SSL/TLS → Edge Certificates, ative estes 3 switches:
- Always Use HTTPS — força HTTP → HTTPS para todo mundo.
- Automatic HTTPS Rewrites — corrige links antigos
http://no HTML. - Minimum TLS Version: selecione 1.2.
Depois vá em Speed → Optimization e ative:
- Auto Minify → marque JavaScript, CSS e HTML.
- Brotli → ligado.
Toda vez que publicar: limpar o cache
A Cloudflare guarda uma cópia dos arquivos. Depois de um pm2 restart, faça:
No navegador, aperte Ctrl+F5 para pegar a nova versão.
(Opcional) Mostrar o IP real do visitante no Nginx
Com o proxy laranja, o Nginx passa a receber os IPs da Cloudflare em vez do IP real. Para consertar, rode uma única vez na VPS:
sudo bash -c 'cat > /etc/nginx/cloudflare-realip.conf <<EOF
$(for ip in $(curl -s https://www.cloudflare.com/ips-v4); do echo "set_real_ip_from $ip;"; done)
$(for ip in $(curl -s https://www.cloudflare.com/ips-v6); do echo "set_real_ip_from $ip;"; done)
real_ip_header CF-Connecting-IP;
EOF'
# inclui o arquivo dentro do bloco http { } do nginx.conf
sudo sed -i '/http {/a \ include /etc/nginx/cloudflare-realip.conf;' /etc/nginx/nginx.conf
# valida e recarrega
sudo nginx -t && sudo systemctl reload nginxRode somente uma vez. Se rodar de novo, vai duplicar a linha include no nginx.conf.
Testar se a Cloudflare está ativa
- Abra o site no navegador.
- Botão direito → Inspecionar → aba Network.
- Aperte F5.
- Clique na primeira requisição (o próprio domínio).
- Em Response Headers, procure:
server: cloudflare
cf-cache-status: HIT # HIT = veio do cache; MISS = veio da VPS
cf-ray: 8f2a1b3c4d5e-GRU✅ Se aparecer server: cloudflare, está funcionando.
🏆 Teste também em ssllabs.com/ssltest — nota esperada: A ou A+.
Erros comuns e como resolver
| Erro | Causa | Solução |
|---|---|---|
| ERR_TOO_MANY_REDIRECTS | SSL Flexible ligado | Trocar para Full (strict) no passo 5 |
| Error 521 · Web server is down | App Node caiu | pm2 restart membresia360 |
| Error 522 · Connection timed out | Firewall bloqueia Cloudflare | sudo ufw allow 443 && sudo ufw allow 80 |
| Error 525 · SSL handshake failed | Certbot ainda não rodou | Rode o Passo 16 antes de marcar Full (strict) |
| Nameservers ainda não trocaram | Propagação lenta | Aguarde e clique em Recheck nameservers |
Vários domínios próprios no mesmo IP (sem subdomínio)
Você não precisa usar subdomínio para hospedar outro site na mesma VPS. O mesmo IP pode atender domínios completamente diferentes (igreja1.com.br, igreja2.com.br, outraigreja.org...). Quem decide qual site entregar é o Nginx, olhando o nome do domínio que veio no pedido (cabeçalho Host).
1. Apontar o DNS do novo domínio para o mesmo IP
No registrador do segundo domínio (Registro.br, GoDaddy, Hostinger...), crie dois registros do tipo A apontando para o mesmo IP da VPS:
Tipo: A Nome: @ Valor: 91.98.120.163
Tipo: A Nome: www Valor: 91.98.120.163Troque 91.98.120.163 pelo IP real da sua VPS. Não é preciso mexer no DNS do primeiro domínio — os dois convivem no mesmo servidor.
2. Ter o app rodando em uma porta exclusiva
O Membresia 360 principal usa a porta 3000. Cada novo site precisa de uma porta diferente (3001, 3002...). Se o segundo site ainda não existe na VPS:
cd /var/www/membresia360 && sudo bash public/scripts/setup-second-site.sh https://github.com/SEU-USUARIO/SEU-REPO.git /var/www/PASTA-DO-SITE 3001 NOME-PM2SEU-REPO = repositório Git do site · PASTA-DO-SITE = pasta de destino em /var/www · 3001 = porta local exclusiva · NOME-PM2 = nome do processo no PM2.
Se você quer o mesmo Membresia 360 respondendo em outro domínio (mesma instalação, mesmo banco), pule esta etapa e use a porta 3000 no passo seguinte.
3. Criar o server block e emitir o SSL — script pronto
O script add-domain.sh valida o domínio e a porta, confere se o DNS já aponta para esta VPS, cria a configuração do Nginx (domínio + www), recarrega o serviço, emite o certificado com o Certbot e deixa a renovação automática ativa:
cd /var/www/membresia360 && sudo bash public/scripts/add-domain.sh DOMINIO.COM.BR PORTA_LOCAL SEU-EMAIL@DOMINIO.COMDOMINIO.COM.BR = o novo domínio, sem http:// e sem barra final · PORTA_LOCAL = porta onde o app roda no PM2 (ex.: 3001) · SEU-EMAIL@DOMINIO.COM = e-mail que o Let's Encrypt usa para avisar sobre expiração.
Exemplo real:
cd /var/www/membresia360 && sudo bash public/scripts/add-domain.sh igreja2.com.br 3001 neuzinaldo@gmail.com4. Conferir se ficou tudo certo
sudo nginx -t
pm2 status
sudo certbot certificates
sudo certbot renew --dry-run
systemctl status certbot.timer --no-pager
curl -I https://igreja2.com.br- Certbot: DNS problem / NXDOMAIN — o DNS ainda não propagou. Espere e rode o script de novo.
- 502 Bad Gateway — nada rodando na porta informada. Confira com
pm2 status. - Abre o site errado — dois server blocks com o mesmo
server_nameou o domínio caiu nodefault_server. Rodesudo bash public/scripts/list-sites.sh. - Rodar o script sem argumentos — ele mostra o modo de uso e sai; informe os três parâmetros.
5. Página de gerenciamento no próprio app
Para cadastrar os domínios, escolher a porta e revisar Nginx/SSL antes de ativar, use a página dedicada — ela monta os comandos prontos para copiar:
| Domínio | Porta | PM2 | SSL |
|---|---|---|---|
| apachedesenvolvedor.com.br | 3000 | membresia360 | Certbot |
| igreja2.com.br | 3001 | igreja2 | Certbot |
| outraigreja.org | 3002 | outraigreja | Certbot |
Lembre: rode tudo dentro de /var/www/membresia360 e, depois do deploy, dê Ctrl+F5 no navegador.
Instalar sem domínio, apontar depois no Registro.br
Você pode instalar o aplicativo primeiro e só depois comprar/configurar o domínio. Essa é a ordem ideal quando ainda não tem o domínio em mãos: faz a VPS funcionar no IP público, testa o app, e depois aponta o domínio para o mesmo IP.
3000 · 2) acesse viahttp://SEU-IP:3000 · 3) depois compre o domínio · 4) aponte A @ e A www no Registro.br · 5) rode o script add-domain.sh para gerar Nginx + SSL.1. Instalar a aplicação sem domínio
Siga os passos 1 a 8 (ou o script de 1 clique) sem se preocupar com o domínio. Quando chegar no .env, você pode deixar a URL vazia ou usar o IP temporário:
# .env (exemplo sem domínio)
VITE_SUPABASE_URL=https://dyjeylwbxbcvpolnwutd.supabase.co
VITE_SUPABASE_ANON_KEY=sua_chave_anon
SITE_URL=http://SEU_IP_AQUI:3000
PUBLIC_SITE_URL=http://SEU_IP_AQUI:3000
PORT=3000SEU_IP_AQUI = o IP público da VPS (ex.: 91.98.120.163). Substitua no.env e reinicie o PM2 depois.
Suba o app com PM2 na porta 3000:
cd /var/www/membresia360
NITRO_PRESET=node-server npm run build
pm2 start ".output/server/index.mjs" --name membresia360 --env PORT=3000
pm2 save2. Liberar a porta 3000 no firewall (temporário)
Para acessar pelo navegador usando o IP, abra a porta 3000 — mas só até configurar o domínio, depois feche novamente:
sudo ufw allow 3000/tcp
sudo ufw reloadTeste no navegador:
http://SEU_IP_AQUI:30003. Comprar o domínio e apontar no Registro.br
Depois que comprar o domínio (ex.: membresia360.com.br), acesse o painel doRegistro.br e crie dois registros do tipo A apontando para o IP da VPS:
Tipo: A Nome: @ Valor: SEU_IP_AQUI
Tipo: A Nome: www Valor: SEU_IP_AQUIObservação: se o painel do Registro.br oferecer a opção de redirecionamentowww, prefira o registro A para que o SSL funcione corretamente.

A propagação pode levar de alguns minutos até 24h. Você pode acompanhar pelo comando:
dig membresia360.com.br +short
ping membresia360.com.br4. Criar o server block e SSL para o domínio novo
Assim que o DNS estiver apontado, rode o script pronto para gerar a configuração Nginx e o certificado SSL gratuito para o domínio na porta 3000:
cd /var/www/membresia360 && sudo bash public/scripts/add-domain.sh membresia360.com.br 3000 neuzinaldo@gmail.commembresia360.com.br = seu domínio · 3000 = porta do app ·neuzinaldo@gmail.com = e-mail para aviso de expiração do certificado.
5. Atualizar o .env com o domínio real
Edite o .env e troque o IP pela URL com https://:
SITE_URL=https://membresia360.com.br
PUBLIC_SITE_URL=https://membresia360.com.brReinicie o PM2 para aplicar:
cd /var/www/membresia360 && NITRO_PRESET=node-server npm run build && pm2 restart membresia360 && pm2 save6. Fechar a porta 3000 no firewall (importante)
Depois do Nginx + SSL funcionando, feche o acesso direto à porta 3000. O tráfego deve passar apenas pelo Nginx nas portas 80 e 443:
sudo ufw delete allow 3000/tcp
sudo ufw allow 'Nginx Full'
sudo ufw reload7. Verificar se está tudo certo
sudo nginx -t
sudo systemctl reload nginx
pm2 status
sudo certbot certificates
sudo certbot renew --dry-run
systemctl status certbot.timer --no-pager
https://membresia360.com.brLembre: rode tudo dentro de /var/www/membresia360 e, depois do deploy, dê Ctrl+F5 no navegador.
Atualizar o app na porta 3001
Sempre que você publicar uma nova versão do Lovable, a segunda instância (domínio membresia360.com.br, porta 3001) precisa ser atualizada manualmente dentro da VPS. O processo é o mesmo da instalação principal, só muda a pasta e o nome do processo no PM2.
1. Entrar na VPS via SSH
Use o terminal do seu computador (Windows: PowerShell ou Git Bash; Mac/Linux: Terminal). Substitua SEU-IP pelo IP da VPS (exemplo: 91.98.120.163) e o usuário padrão do provedor (geralmente root ou ubuntu):
ssh root@91.98.120.163A senha ou chave SSH é a mesma que você recebeu no e-mail da hospedagem quando contratou a VPS.
2. Atualização normal (comando em uma linha)
Rode tudo dentro da pasta /var/www/membresia360-m360. Esse comando puxa o código novo do GitHub, instala dependências, faz o build de produção e reinicia o PM2:
cd /var/www/membresia360-m360 && git pull && npm install && NITRO_PRESET=node-server npm run build && pm2 restart membresia360-m360 && pm2 saveDica: se o processo membresia360-m360 ainda não existir no PM2, use a versão robusta abaixo que cria o processo automaticamente.
cd /var/www/membresia360-m360 && export PORT=3001 && git pull && npm install && NITRO_PRESET=node-server npm run build && (pm2 restart membresia360-m360 || pm2 start .output/server/index.mjs --name membresia360-m360) && pm2 saveA variável PORT=3001 garante que o app suba na porta correta.
3. Atualização forçada (quando a normal não funciona)
Use quando o site continuar desatualizado ou o processo aparecer como errored no pm2 status. Esse comando limpa caches e ignora conflitos no Git:
cd /var/www/membresia360-m360 && export PORT=3001 && git fetch origin && git reset --hard origin/main && rm -rf .output node_modules/.vite dist && npm install && NITRO_PRESET=node-server npm run build && (pm2 restart membresia360-m360 || pm2 start .output/server/index.mjs --name membresia360-m360) && pm2 save4. Scripts prontos para download
Se preferir, baixe os scripts abaixo e execute na VPS. Eles já vêm com a pasta, porta e nome do processo pré-configurados:
5. Verificar se deu certo
pm2 status membresia360-m360
# Deve aparecer online na coluna status
curl -s http://127.0.0.1:3001/ | head -n 20Lembre-se: após atualizar, abra o navegador e aperte Ctrl+F5 para limpar o cache. A maior parte dos casos de "site antigo" é apenas cache do navegador.
Autenticação do GitHub na VPS (PAT)
Se você acabou de rodar um git pull ou um script de atualização e o terminal parou pedindo Username for 'https://github.com', isso acontece porque o repositório é privado e o GitHub não aceita mais senha via HTTPS. A solução é usar um Personal Access Token (PAT).
1. Como gerar o PAT no GitHub
- Acesse https://github.com/settings/tokens
- Clique em Generate new token (classic)
- Dê um nome (ex.:
VPS Membresia 360) - Marque a permissão repo (ela dá acesso total a repositórios privados)
- Clique em Generate token e copie o token gerado
Erro comum: Invalid username or token
Na VPS não use @secret:GITHUB_PERSONAL_ACCESS_TOKEN. Esse texto só funciona dentro do Lovable. No terminal da VPS você precisa colar o token real do GitHub, que normalmente começa com ghp_.
cd /var/www/membresia360
git remote set-url origin https://github.com/jogosdaunidade/apache-dev.git
git config --local credential.helper store
read -p "Usuário GitHub: " GITHUB_USER
read -s -p "Token GitHub: " GITHUB_PAT; echo
printf 'protocol=https
host=github.com
username=%s
password=%s
' "$GITHUB_USER" "$GITHUB_PAT" | git credential approve
unset GITHUB_PAT
git pullDepois que o git pull funcionar, rode o deploy normal novamente.
2. Configurar o token na VPS (instância principal)
Substitua SEU_PAT_AQUI pelo token que você copiou e rode dentro da VPS:
cd /var/www/membresia360 && sudo bash public/scripts/setup-github-token.sh SEU_PAT_AQUI3. Configurar o token na segunda instância (porta 3001)
Se você também instalou o membresia360.com.br na pasta /var/www/membresia360-m360, rode o mesmo script apontando para essa pasta:
cd /var/www/membresia360-m360 && sudo bash /var/www/membresia360/public/scripts/setup-github-token.sh SEU_PAT_AQUI /var/www/membresia360-m3604. Verificar se funcionou
Depois de configurar o token, teste o git pull na mesma pasta. Se não pedir usuário/senha, está tudo certo:
cd /var/www/membresia360 && git pull5. (Recomendado) git pull automático sem token na URL
O método acima grava o token dentro da URL do remote. A forma mais segura é guardar o PAT em um arquivo de ambiente protegido e deixar o script git-pull-auto.sh ler esse arquivo na hora do git pull.
5.1 — Criar o arquivo de ambiente (uma única vez):
sudo mkdir -p /etc/membresia360 && sudo nano /etc/membresia360/git.envCole dentro do arquivo (troque pelos seus dados):
GITHUB_USER=SEU_USUARIO_GITHUB
GITHUB_PAT=SEU_PAT_AQUI- GITHUB_USER — seu nome de usuário no GitHub (ex.:
jogosdaunidade) - GITHUB_PAT — o token gerado no passo 1 (começa com
ghp_)
5.2 — Proteger o arquivo (só o root pode ler):
sudo chmod 600 /etc/membresia360/git.env5.3 — Rodar o pull automático na instância principal (porta 3000):
cd /var/www/membresia360 && sudo bash public/scripts/git-pull-auto.sh5.4 — Na segunda instância (porta 3001):
cd /var/www/membresia360 && sudo bash public/scripts/git-pull-auto.sh /var/www/membresia360-m360- PASTA_DO_APP — caminho do app que será atualizado (padrão
/var/www/membresia360)
git remote -v continua limpo (sem token visível), o PAT não aparece no histórico do shell e o arquivo git.env fica com permissão 600. Para trocar o token, basta editar esse arquivo — nada mais precisa mudar.Lembre: rode tudo dentro de /var/www/membresia360 e, depois do deploy, dê Ctrl+F5 no navegador.
Entrar em cada porta (3000 e 3001)
Para não se confundir entre as instâncias, use os comandos abaixo para entrar na pasta correta de cada app. Sempre rode os comandos de atualização dentro da pasta do app.
Instância principal — apachedesenvolvedor.com.br (porta 3000):
cd /var/www/membresia360Segunda instância — membresia360.com.br (porta 3001):
cd /var/www/membresia360-m360Verificar qual processo está em cada porta:
pm2 status
ss -ltn | grep -E ':3000|:3001'Dica: depois de entrar na pasta, use os scripts de atualização do passo 22 (porta 3001) ou do passo 28 (atualização forçada).
Firewall e portas de acesso (80, 443, 3000, 3001)
Mesmo com o app rodando no PM2, o firewall pode bloquear o acesso externo. Confirme que as portas 80, 443, 3000 e 3001 estão liberadas tanto no UFW quanto no firewall do provedor de VPS.
1 — Verificar o status atual do firewall:
sudo ufw status verbose2 — Liberar HTTP, HTTPS e Nginx:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 'Nginx Full'
sudo ufw reload3 — Liberar as portas internas dos apps (3000 e 3001):
sudo ufw allow 3000/tcp
sudo ufw allow 3001/tcp
sudo ufw reload4 — Testar se o app responde localmente:
curl -sSI http://127.0.0.1:3000/ | head -n 1
curl -sSI http://127.0.0.1:3001/ | head -n 15 — Testar acesso externo (substitua pelo IP da VPS):
curl -sSI http://91.98.120.163:3000/ | head -n 1
curl -sSI http://91.98.120.163:3001/ | head -n 16 — Se estiver usando outro firewall (ex.: iptables, cloud provider):
# Ver regras ativas
sudo iptables -L -n -v | grep -E '80|443|3000|3001'Lembre: rode tudo dentro de /var/www/membresia360 e, depois do deploy, dê Ctrl+F5 no navegador.
SSL do membresia360.com.br (porta 3001)
O domínio já está registrado e apontado para o IP 91.98.120.163. Agora é instalar o certificado HTTPS e publicar a instância da porta 3001.
1 — Instalar o SSL com Certbot (Nginx + certificado automáticos):
cd /var/www/membresia360 && sudo bash public/scripts/add-domain.sh membresia360.com.br 3001 neuzinaldo@gmail.com- membresia360.com.br — domínio próprio já apontado para o IP da VPS
- 3001 — porta local onde a segunda instância roda no PM2
- neuzinaldo@gmail.com — e-mail do Let's Encrypt para avisos de expiração
2 — Verificar se o certificado foi emitido:
sudo certbot certificates3 — Atualizar o .env da instância 3001 com a URL definitiva:
cd /var/www/membresia360-m360 && nano .envAltere (ou adicione) a linha:
VITE_PUBLIC_SITE_URL=https://membresia360.com.br4 — Recompilar e reiniciar o app:
cd /var/www/membresia360-m360 && NITRO_PRESET=node-server npm run build && pm2 restart membresia360-m360 && pm2 save5 — Testar no navegador:
https://membresia360.com.brdig membresia360.com.br +short (deve retornar 91.98.120.163), verifique se a porta 3001 responde com curl -sSI http://127.0.0.1:3001/ | head -n 1 e só então rode o script de novo.Lembre: rode tudo dentro de /var/www/membresia360 e, depois do deploy, dê Ctrl+F5 no navegador.
SSL do apachedesenvolvedor.com.br (porta 3000)
Mesmo procedimento do passo 26, agora para a instância principal que roda na porta 3000 em /var/www/membresia360.
1 — Instalar o SSL com Certbot (Nginx + certificado automáticos):
cd /var/www/membresia360 && sudo bash public/scripts/add-domain.sh apachedesenvolvedor.com.br 3000 neuzinaldo@gmail.com- apachedesenvolvedor.com.br — domínio próprio já apontado para o IP da VPS
- 3000 — porta local onde a instância principal roda no PM2
- neuzinaldo@gmail.com — e-mail do Let's Encrypt para avisos de expiração
2 — Verificar se o certificado foi emitido:
sudo certbot certificates3 — Atualizar o .env da instância 3000 com a URL definitiva:
cd /var/www/membresia360 && nano .envAltere (ou adicione) a linha:
VITE_PUBLIC_SITE_URL=https://apachedesenvolvedor.com.br4 — Recompilar e reiniciar o app:
cd /var/www/membresia360 && NITRO_PRESET=node-server npm run build && pm2 restart membresia360 && pm2 save5 — Testar no navegador:
https://apachedesenvolvedor.com.brdig apachedesenvolvedor.com.br +short (deve retornar 91.98.120.163), verifique se a porta 3000 responde com curl -sSI http://127.0.0.1:3000/ | head -n 1 e só então rode o script de novo.Lembre: rode tudo dentro de /var/www/membresia360 e, depois do deploy, dê Ctrl+F5 no navegador.
Atualização forçada (portas 3000 e 3001)
Use quando o git pull normal não muda nada no site (cache de build antigo ou conflito no Git). O comando descarta alterações locais e reconstrói do zero.
git reset --hard apaga alterações feitas direto na VPS.Porta 3000 — instância principal (/var/www/membresia360):
cd /var/www/membresia360 && sudo bash public/scripts/update-force.shOu manualmente:
cd /var/www/membresia360 && git fetch origin && git reset --hard origin/main && rm -rf .output node_modules/.vite dist && npm install && NITRO_PRESET=node-server npm run build && pm2 restart membresia360 && pm2 savePorta 3001 — instância membresia360.com.br (/var/www/membresia360-m360):
cd /var/www/membresia360 && sudo bash public/scripts/update-force-m360.shOu manualmente:
cd /var/www/membresia360-m360 && git fetch origin && git reset --hard origin/main && rm -rf .output node_modules/.vite dist && npm install && NITRO_PRESET=node-server npm run build && pm2 restart membresia360-m360 && pm2 saveAs duas de uma vez:
cd /var/www/membresia360 && sudo bash public/scripts/update-force.sh && sudo bash public/scripts/update-force-m360.sh && pm2 statusErro: "No such file or directory" ao rodar update-force-m360.sh
Isso acontece quando a VPS ainda não baixou o script novo do GitHub. Atualize o repositório primeiro e depois rode o script:
cd /var/www/membresia360 && git fetch origin && git reset --hard origin/main && chmod +x public/scripts/*.sh && sudo bash public/scripts/update-force-m360.shSe o script ainda não existir, use o comando manual da porta 3001 logo acima.
Depois do deploy, dê Ctrl+F5 no navegador para limpar o cache.
Instalar um site novo com 1 comando
Este é o comando "faz tudo": ele instala o que faltar (Node 20, Nginx, PM2, Certbot), escolhe uma porta livre automaticamente (3002, 3003, ... — as suas 3000 e 3001 continuam intocadas), clona o repositório, cria o .env, faz o build, sobe no PM2, configura o Nginx e ainda tenta emitir o SSL. Depois disso só falta o apontamento DNS no Registro.br.
REPO_GIT— URL do repositório no GitHubDOMINIO— domínio do site (ex.: meusite.com.br)NOME— nome da pasta em /var/www e do processo no PM2PORTA— opcional; deixeautopara o script escolher sozinhoEMAIL— opcional; usado no certificado SSL
Comando único (baixa o script direto e executa):
curl -fsSL https://membresia360.com.br/scripts/deploy-site.sh -o /tmp/deploy-site.sh && sudo bash /tmp/deploy-site.sh REPO_GIT DOMINIO NOME auto SEU@EMAIL.COMExemplo real:
curl -fsSL https://membresia360.com.br/scripts/deploy-site.sh -o /tmp/deploy-site.sh && sudo bash /tmp/deploy-site.sh https://github.com/jogosdaunidade/relatorio-estadual.git relatorioestadual.com.br relatorio auto neuzinaldo@gmail.comSe o projeto já estiver clonado na VPS, use o script local:
cd /var/www/membresia360 && sudo bash public/scripts/deploy-site.sh REPO_GIT DOMINIO NOME auto SEU@EMAIL.COMO que ainda fica manual (Registro.br):
- Registro.br → Domínios → Editar Zona
A· Nome@· Valor91.98.120.163· TTL 3600A· Nomewww· Valor91.98.120.163· TTL 3600- Salvar e aguardar a propagação (15 min a algumas horas).
Se o DNS ainda não tiver propagado quando você rodar o script, ele instala tudo e avisa. Depois que propagar, é só emitir o SSL:
sudo certbot --nginx -d DOMINIO -d www.DOMINIO --agree-tos -m SEU@EMAIL.COM --redirectConferir tudo o que está rodando:
pm2 status && sudo nginx -t && ss -ltnp | grep -E ':(3000|3001|3002|3003)'O script nunca reutiliza uma porta ocupada: se 3002 estiver em uso, ele pula para 3003 e assim por diante. Suas instâncias das portas 3000 e 3001 não são tocadas.
Card de instalação rápida com exemplos
Use este passo como um cartão de referência: copie o modelo, troque os placeholders e cole no terminal da VPS. O mesmo script do Passo 29, só que aqui com exemplos reais dos repositórios que você já usou.
Substitua pelos seus dados. O script detecta sozinho a porta livre (a partir da 3002, sem tocar nas suas 3000/3001), clona, builda, sobe no PM2 e configura Nginx + SSL.
Meu repositório do GitHub (substitua os dados):
curl -fsSL https://membresia360.com.br/scripts/deploy-site.sh -o /tmp/deploy-site.sh && sudo bash /tmp/deploy-site.sh REPO_GIT DOMINIO NOME auto SEU@EMAIL.COMComo substituir pelos seus dados:
REPO_GIT— cole aqui o link do seu repositório no GitHub. Exemplo:https://github.com/seu-usuario/seu-projeto.gitDOMINIO— domínio que você comprou no Registro.br. Exemplo:seusite.com.brNOME— nome curto para a pasta e processo PM2. Exemplo:meusite(vai criar/var/www/meusite)SEU@EMAIL.COM— e-mail para receber avisos do certificado SSL (Let’s Encrypt)
Exemplo pronto para copiar e colar:
curl -fsSL https://membresia360.com.br/scripts/deploy-site.sh -o /tmp/deploy-site.sh && sudo bash /tmp/deploy-site.sh https://github.com/seu-usuario/seu-projeto.git seusite.com.br meusite auto neuzinaldo@gmail.comSó trocar seu-usuario/seu-projeto, seusite.com.br e meusite pelos seus dados.
Checklist depois de rodar o comando:
- Verifique se o processo subiu:
pm2 status - Confira a porta:
ss -ltnp | grep -E ':(3000|3001|3002|3003)' - Teste pelo IP:
http://91.98.120.163:PORTA - Registro.br →
A @ 91.98.120.163eA www 91.98.120.163 - Se o SSL não foi emitido automaticamente, rode o Certbot manualmente após a propagação do DNS.
Exemplo pronto — instalando um repositório seu do GitHub:
curl -fsSL https://membresia360.com.br/scripts/deploy-site.sh -o /tmp/deploy-site.sh && sudo bash /tmp/deploy-site.sh https://github.com/jogosdaunidade/relatorio-estadual.git relatorioestadual.com.br relatorio auto neuzinaldo@gmail.comSó trocar jogosdaunidade/relatorio-estadual, relatorioestadual.com.br e relatorio pelos seus dados.
Lembre-se: rode dentro da VPS via SSH. Depois de qualquer mudança, dê Ctrl+F5 no navegador.
Trocar o site de uma porta por outro repositório
Use este passo quando você já tem um site rodando em uma porta (ex.: 3000) e quer substituí-lo por um repositório diferente do GitHub — mantendo a mesma porta, o mesmo processo PM2 e o mesmo domínio no Nginx. O script faz backup da pasta atual, clona o novo repositório, copia o .env antigo, builda e reinicia no PM2.
Comando pronto — trocar o site da porta 3000 pelo repositório apache-dev:
cd /var/www/membresia360 && sudo bash public/scripts/replace-site.sh https://github.com/jogosdaunidade/apache-dev.git membresia360 3000Modelo com placeholders:
cd /var/www/membresia360 && sudo bash public/scripts/replace-site.sh REPO_GIT PM2_NAME PORTAO que cada placeholder significa:
REPO_GIT— URL do novo repositório no GitHubPM2_NAME— nome do processo atual (ex.:membresia360)PORTA— porta que o Nginx já aponta (ex.:3000)
Se o repositório novo for privado, configure o PAT antes:
cd /var/www/membresia360 && sudo bash public/scripts/setup-github-token.sh SEU_PAT_AQUIPara restaurar o backup manualmente (em caso de erro):
pm2 delete membresia360 || true && sudo rm -rf /var/www/membresia360 && sudo mv /var/www/membresia360-backup-XXXX /var/www/membresia360 && cd /var/www/membresia360 && pm2 start ".output/server/index.mjs" --name membresia360 && pm2 saveSubstitua XXXX pelo sufixo do nome do backup. Liste os backups com:ls -d /var/www/membresia360-backup-*.
Verificar depois:
pm2 status && ss -ltnp | grep -E ':(3000|3001)' && sudo nginx -tDepois do deploy, dê Ctrl+F5 no navegador para limpar o cache.
Pasta de backup e como restaurar
Quando você troca o site de uma porta por outro repositório (Passo 31), o script replace-site.sh cria automaticamente uma pasta de backup com a data e hora da operação. Exemplo: /var/www/membresia360-backup-20260801-210941.
Para que serve essa pasta?
- É uma cópia de segurança do projeto anterior, incluindo o build, dependências e configurações locais (exceto o
.env, que é copiado para o novo projeto). - Serve para reverter rapidamente se o novo repositório não buildar, apresentar erro 502 ou comportamento inesperado.
- Permite consultar arquivos antigos, comparar versões ou recuperar dados que não foram migrados para o novo projeto.
Como listar os backups existentes:
ls -ld /var/www/membresia360-backup-*Como restaurar o backup mais recente (exemplo na porta 3000):
BACKUP=$(ls -d /var/www/membresia360-backup-* | sort | tail -1) && pm2 delete membresia360 || true && sudo rm -rf /var/www/membresia360 && sudo mv "$BACKUP" /var/www/membresia360 && cd /var/www/membresia360 && pm2 start ".output/server/index.mjs" --name membresia360 && pm2 save && sudo nginx -t && sudo systemctl reload nginxEsse comando pega o backup mais recente automaticamente, sem precisar digitar o sufixo.
Restauração manual com nome exato do backup:
pm2 delete membresia360 || true && sudo rm -rf /var/www/membresia360 && sudo mv /var/www/membresia360-backup-20260801-210941 /var/www/membresia360 && cd /var/www/membresia360 && pm2 start ".output/server/index.mjs" --name membresia360 && pm2 save && sudo nginx -t && sudo systemctl reload nginxSubstitua 20260801-210941 pelo sufixo real do seu backup (veja no ls -ld acima).
Verificar se voltou ao normal:
pm2 status && ss -ltnp | grep -E ':(3000|3001)' && sudo nginx -t && curl -I http://localhost:3000Limpar backups antigos (opcional — cuidado!):
sudo ls -td /var/www/membresia360-backup-* | tail -n +4 | xargs -r sudo rm -rfMantém os 3 backups mais recentes e apaga os mais antigos. Verifique a lista antes de executar.
Depois de restaurar, dê Ctrl+F5 no navegador para limpar o cache.
Imagens não aparecem (erro 404 em /__l5e/)
Sintoma: no domínio apachedesenvolvedor.com.br o site abre normalmente na VPS, o layout carrega, mas as imagens dos cards / logos / banners ficam vazias. No código-fonte da página aparecem caminhos assim:
<img src="/__l5e/assets-v1/864baa83-.../apache-logo-transparent.png">E ao abrir esse endereço direto no navegador retorna 404.
Por que isso acontece em apachedesenvolvedor.com.br?
- As imagens não estão dentro do build: são assets hospedados no CDN da Lovable (arquivos
.asset.jsonno repositório). - O caminho
/__l5e/só existe na infraestrutura da Lovable (*.lovable.app). Na sua VPS (Nginx + PM2) esse endereço não existe → 404. - CSS e JS funcionam porque ficam em
/assets/, gerados pelo build local.
Diagnóstico rápido:
curl -s -o /dev/null -w "%{http_code}\n" https://apachedesenvolvedor.com.br/__l5e/assets-v1/ID/ARQUIVO.pngSe retornar 404, é exatamente esse problema.
Como comparar dois domínios e descobrir onde falta o proxy
Se você tem um domínio que exibe as imagens corretamente (por exemplo membresia360.com.br) e outro que não exibe (por exemplo apachedesenvolvedor.com.br), o motivo geralmente é que o primeiro já possui o bloco location /__l5e/ no Nginx ou ainda está apontando para a infraestrutura da Lovable. Já o segundo foi trocado para outro projeto, então os IDs dos assets/__l5e/ não existem na origem anterior.
- Veja se cada domínio possui o bloco
location /__l5e/:grep -R "location /__l5e/" /etc/nginx/sites-enabled/ -A 3 -B 1 - Descubra para qual origem Lovable cada domínio aponta:
grep -R "proxy_pass" /etc/nginx/sites-enabled/ | grep -E "membresia360|apachedesenvolvedor" - Teste uma mesma imagem nos dois domínios (pegue o caminho exato no inspecionar do navegador):
IMG="/__l5e/assets-v1/SEU-ID/arquivo.png" for DOM in membresia360.com.br apachedesenvolvedor.com.br; do echo -n "$DOM: " curl -s -o /dev/null -w "%{http_code} (%{content_type})\n" "https://$DOM$IMG" done - Se o domínio que funciona apontar para um projeto Lovable (ex.:
membresia360.com.br) e o domínio quebrado agora roda um projeto diferente (ex.:apache-dev), o proxy dele deve apontar para a URL Lovable daquele projeto específico, ou os assets devem ser auto-hospedados.
Se ambos os domínios forem sites diferentes, é normal e esperado que os assets diverjam. Nesse caso, o Passo 33 deve ser aplicado separadamente em cada domínio, usando a origem Lovable correta do respectivo projeto.
Solução 1 — Proxy no Nginx (rápido, 1 minuto)
Encaminha /__l5e/ para o domínio Lovable do projeto. Edite o arquivo do site:
sudo nano /etc/nginx/sites-available/apachedesenvolvedor.com.brDentro do bloco server { ... } (antes do location /), adicione:
location /__l5e/ {
proxy_pass https://membresia360.com.br/;
proxy_set_header Host membresia360.com.br;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ssl_server_name on;
proxy_cache_valid 200 7d;
add_header Cache-Control "public, max-age=604800";
}Importante: o proxy_pass precisa terminar com / para que o Nginx reescreva corretamente o caminho. Depois teste e recarregue:
sudo nginx -t && sudo systemctl reload nginxapachedesenvolvedor.com.br— domínio próprio hospedado na VPS.membresia360.com.br— URL publicada do projeto na Lovable (fonte real dos assets).
Solução 2 — Definitiva (recomendada)
No projeto que roda nessa porta, baixe as imagens para src/assets/ e importe como módulo ES6, removendo os arquivos .asset.json:
import logo from "@/assets/apache-logo.png";
// <img src={logo} alt="apache.dev" />Assim as imagens entram no build, ficam auto-hospedadas na VPS e o site deixa de depender do CDN da Lovable. Depois: git pull + build + pm2 restart.
Após aplicar qualquer uma das soluções, dê Ctrl+F5 no navegador para limpar o cache.
Painel /campanhas: erro de Supabase ou Meta
Sintomas comuns em https://membresia360.com.br/campanhas#painel-meta:
Missing Supabase environment variable(s): SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY. Connect Supabase in Lovable Cloud.Credenciais do Meta ausentes. Configure META_APP_ID, META_APP_SECRET, META_ACCESS_TOKEN, META_AD_ACCOUNT_ID e META_PAGE_ID.Causa: na Lovable essas variáveis são injetadas automaticamente. Na VPS o processo do PM2 sobe sem carregar o arquivo .env, então o servidor não enxerga SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY nem os tokens do Meta (META_ACCESS_TOKEN, META_APP_SECRET etc.).
Solução com 1 comando (script pronto)
cd /var/www/membresia360 && sudo bash public/scripts/env-vps.sh APP_DIR APP_NAME- APP_DIR — pasta do site na VPS (ex.:
/var/www/membresia360-m360para o domínio membresia360.com.br). - APP_NAME — nome do processo no PM2 (ex.:
membresia360-m360). Veja compm2 status.
Exemplo para o domínio membresia360.com.br (porta 3001):
cd /var/www/membresia360 && sudo bash public/scripts/env-vps.sh /var/www/membresia360-m360 membresia360-m360E para a instância principal (porta 3000):
cd /var/www/membresia360 && sudo bash public/scripts/env-vps.sh /var/www/membresia360 membresia360Erro comum: "No such file or directory"
Se o script env-vps.sh ainda não existir na VPS, é porque o último deploy não foi baixado. Rode um git pull na instância que tem o arquivo atualizado ou use o caminho absoluto da instância principal:
cd /var/www/membresia360 && git pull && sudo bash public/scripts/env-vps.sh /var/www/membresia360-m360 membresia360-m360Se o domínio membresia360.com.br estiver na pasta /var/www/membresia360-m360, o script pode ser executado de lá mesmo depois de dar git pull na pasta /var/www/membresia360.
Tokens do Meta ficam em .env.local
Na primeira execução o script cria um modelo .env.local dentro da pasta do site. Preencha com os valores reais e rode o script novamente. Os valores abaixo já foram configurados no ambiente Lovable; na VPS você precisa copiá-los para o .env.local da pasta correta:
sudo nano /var/www/membresia360-m360/.env.localMETA_APP_ID=2149953112262997
META_APP_SECRET=COLE_A_CHAVE_SECRETA_AQUI
META_ACCESS_TOKEN=COLE_O_TOKEN_EAA_AQUI
META_AD_ACCOUNT_ID=1696814688265849
META_PAGE_ID=1245138122016416Nunca envie META_APP_SECRET nem META_ACCESS_TOKEN no chat público. Esse arquivo nunca vai para o Git e fica com permissão 600.
Erro: "faltando ou ainda com placeholder em .env.local"
Isso significa que o modelo foi criado corretamente, mas ainda está vazio. Abra o arquivo da instância principal, preencha as cinco linhas com valores reais, salve e execute o script novamente. Não escreva COLE_..._AQUI: substitua pelo segredo e token verdadeiros.
cd /var/www/membresia360 && sudo nano .env.localMETA_APP_ID=2149953112262997
META_APP_SECRET=COLE_O_APP_SECRET_REAL
META_ACCESS_TOKEN=COLE_O_TOKEN_EAA_REAL
META_AD_ACCOUNT_ID=1696814688265849
META_PAGE_ID=1245138122016416No nano: pressione Ctrl+O, Enter e Ctrl+X. Depois rode:
cd /var/www/membresia360 && sudo bash public/scripts/env-vps.sh /var/www/membresia360 membresia360Alternativa manual (sem script)
cd APP_DIR
set -a; . ./.env; [ -f .env.local ] && . ./.env.local; set +a
pm2 restart APP_NAME --update-env && pm2 saveComo conferir se resolveu
pm2 env $(pm2 id APP_NAME | tr -d '[] ') | grep -E '^(META_APP_ID|META_APP_SECRET|META_ACCESS_TOKEN|META_AD_ACCOUNT_ID|META_PAGE_ID)[:=]' | sed -E 's/[:=].*/=<definida>/'O resultado precisa mostrar as cinco linhas como <definida>. Se alguma não aparecer, edite o .env.local e execute novamente o script. Depois abra /campanhas#painel-meta logado como administrador e dê Ctrl+F5. Deslogado o painel sempre mostra “Entre com a sua conta de administrador”, o que é normal e não é erro.
Chaves do Meta — copie e cole na VPS
Use este passo para criar o arquivo .env.local na VPS já com os 3 IDs públicos preenchidos. Você só vai precisar colar os 2 segredos do Meta.
1) Modo fácil — script pergunta os segredos (não precisa editar arquivo)
Rode na VPS. O script pede os 2 segredos de forma oculta, cria o arquivo e já recarrega o PM2.
cd /var/www/membresia360 && sudo bash public/scripts/create-env-m360.shSe o script não existir, rode primeiro cd /var/www/membresia360 && git pull.
1-alt) Opção manual — crie o arquivo você mesmo
Se preferir, crie o arquivo direto na VPS (já com os 3 IDs públicos). Depois cole os 2 segredos com o nano.
sudo tee /var/www/membresia360-m360/.env.local > /dev/null <<'EOF'
# Tokens do Meta Ads — IDs públicos já preenchidos; cole apenas os 2 segredos
META_ACCESS_TOKEN=
META_AD_ACCOUNT_ID=1696814688265849
META_APP_ID=2149953112262997
META_APP_SECRET=
META_PAGE_ID=1245138122016416
# Google Ads
GOOGLE_ADS_CONVERSION_ID=
# Opcionais
META_CAMPAIGN_ID=
META_WHATSAPP_NUMBER=
META_DEFAULT_REGION=
VPS_EXEC_TOKEN=
EOF
sudo chmod 600 /var/www/membresia360-m360/.env.local2) Cole os 2 segredos no arquivo
Abra o arquivo com o nano e substitua as linhas vazias de META_APP_SECRET e META_ACCESS_TOKEN pelos valores reais. Não deixe espaços em branco no começo ou no fim.
sudo nano /var/www/membresia360-m360/.env.localNo nano: Ctrl+O, Enter, Ctrl+X para salvar e sair.
Onde pegar cada segredo
- META_APP_SECRET — Configurações → Básico → Chave secreta do aplicativo.
- META_ACCESS_TOKEN — Configurações do Negócio → Usuários do sistema → selecione neuzinaldo → primeiro atribua a conta de anúncios com controle total → gere um novo token do app Membresia 360 com expiração Nunca e permissões
ads_management,ads_read,business_management,pages_show_list→ Gerar token. Um token gerado antes de atribuir a conta deve ser substituído. - META_AD_ACCOUNT_ID — topo do Gerenciador de Anúncios (já preenchido: 1696814688265849).
- META_PAGE_ID — Configurações da Página → Sobre → ID da Página (já preenchido: 1245138122016416).
3) Recarregue o PM2 com as variáveis
cd /var/www/membresia360 && sudo bash public/scripts/env-vps.sh /var/www/membresia360-m360 membresia360-m360Acesse /campanhas#painel-meta logado como admin e dê Ctrl+F5.