Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save insinfo/79aaa95d1c43da0305afa810e461b674 to your computer and use it in GitHub Desktop.

Select an option

Save insinfo/79aaa95d1c43da0305afa810e461b674 to your computer and use it in GitHub Desktop.
migrar do Supabase para Hostinger VPS com Coolify

Tutorial: migrar do Supabase para Hostinger VPS com Coolify

Objetivo: sair do Supabase e rodar um portal de notícias full stack em uma VPS da Hostinger usando Coolify, PostgreSQL, backend Dart Shelf e frontend NgDart 8.

Cenário do projeto:

  • Backend: API REST em Dart com Shelf.
  • Frontend: NgDart 8 compilado como arquivos estáticos.
  • Acesso a dados: biblioteca eloquent.dart, compatível com PostgreSQL e MySQL.
  • Banco de origem: Supabase, baseado em PostgreSQL.
  • Banco recomendado no destino: PostgreSQL.
  • Deploy recomendado: Docker Compose gerenciado pelo Coolify.

1. Visão geral da arquitetura final

Hostinger VPS
├── Coolify
├── PostgreSQL 16
├── Backend Dart Shelf
├── Frontend NgDart 8 estático
├── Proxy/HTTPS do Coolify
└── Backups automáticos

Fluxo recomendado:

Visitante
  ↓
Cloudflare DNS/CDN
  ↓
Hostinger VPS
  ├── Frontend NgDart estático
  ├── API Dart Shelf
  └── PostgreSQL

Para começar, use:

https://seudominio.com.br      → frontend
https://api.seudominio.com.br  → backend API

Depois, se quiser simplificar CORS, você pode migrar para:

https://seudominio.com.br/api  → backend API

2. Escolha da VPS

Para o portal de notícias, recomendo uma VPS com pelo menos:

2 vCPU
4 GB RAM
80 GB SSD/NVMe
Ubuntu 24.04 LTS

O ideal para trabalhar com folga:

2 vCPU
8 GB RAM
100 GB SSD/NVMe
Ubuntu 24.04 LTS

Motivo: o Coolify, PostgreSQL, builds Docker, backend Dart e frontend estático cabem em 4 GB, mas 8 GB evita travamentos durante build e restauração de banco.

Se a Hostinger oferecer o template pronto, escolha:

Ubuntu 24.04 with Coolify

Esse template já vem com Coolify pré-instalado. Se não aparecer, escolha Ubuntu 24.04 LTS limpo e instale o Coolify manualmente.


3. Preparar DNS do domínio

No painel DNS do seu domínio, crie:

Tipo    Nome    Valor
A       @       IP_DA_VPS
A       www     IP_DA_VPS
A       api     IP_DA_VPS

Exemplo:

seudominio.com.br      → IP_DA_VPS
www.seudominio.com.br  → IP_DA_VPS
api.seudominio.com.br  → IP_DA_VPS

Se usar Cloudflare, deixe inicialmente em DNS only durante a primeira configuração. Depois que HTTPS estiver funcionando, você pode ativar o proxy.


4. Instalar ou acessar o Coolify

Opção A: template da Hostinger

Se você criou a VPS usando o template Ubuntu 24.04 with Coolify, acesse o painel da Hostinger e procure as instruções de acesso ao Coolify.

Normalmente o acesso será algo como:

http://IP_DA_VPS:8000

Crie o usuário administrador no primeiro acesso.

Opção B: instalação manual

Entre na VPS por SSH:

ssh root@IP_DA_VPS

Atualize o servidor:

apt update && apt upgrade -y

Instale o Coolify:

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash

Depois acesse:

http://IP_DA_VPS:8000

5. Criar projeto no Coolify

No painel do Coolify:

Projects
  → New Project
  → portal-noticias

Dentro do projeto, você pode separar os recursos assim:

portal-postgres
portal-api
portal-frontend

Para seu caso, eu recomendo usar Docker Compose único, porque a aplicação é full stack e fica mais fácil versionar tudo no Git.


6. Escolha do banco: PostgreSQL ou MySQL?

Como a origem é Supabase, escolha PostgreSQL.

Motivos:

  • Supabase já usa PostgreSQL.
  • A migração é mais direta.
  • Você evita converter tipos, funções e índices.
  • PostgreSQL oferece recursos excelentes para portal de notícias: jsonb, tsvector, índices GIN, views, full-text search e extensões.
  • Sua biblioteca eloquent.dart já funciona com PostgreSQL.

Use MySQL apenas se o projeto ainda não tiver banco no Supabase ou se você tiver um motivo forte de hospedagem/custo. Neste caso, como já existe Supabase, PostgreSQL é o caminho natural.


7. Criar PostgreSQL no Coolify

Você pode criar pelo painel do Coolify ou pelo Docker Compose.

Opção recomendada: via Docker Compose

Exemplo de serviço PostgreSQL:

services:
  postgres:
    image: postgres:16
    container_name: portal-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: portal
      POSTGRES_USER: portal_user
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - portal_postgres_data:/var/lib/postgresql/data
    networks:
      - portal_net

volumes:
  portal_postgres_data:

networks:
  portal_net:

Importante: não exponha a porta 5432 publicamente. A API deve acessar o banco pela rede interna Docker.


8. Exportar o banco do Supabase

Instale a CLI do Supabase na sua máquina local.

Faça login:

supabase login

Entre na pasta do projeto:

cd caminho/do/projeto

Vincule o projeto remoto:

supabase link --project-ref SEU_PROJECT_REF

Gere o dump:

supabase db dump --linked --file supabase_dump.sql

Esse método é melhor do que tentar fazer pg_dump bruto do banco inteiro, porque o Supabase possui schemas internos, roles e permissões próprias.

Se quiser gerar apenas schema público e dados principais, revise a documentação do Supabase e confira se você usa:

auth.users
storage.objects
realtime
edge functions
policies RLS
schemas privados

Se usa apenas tabelas comuns no schema public, a migração tende a ser simples.


9. Copiar o dump para a VPS

Na sua máquina local:

scp supabase_dump.sql root@IP_DA_VPS:/root/supabase_dump.sql

Entre na VPS:

ssh root@IP_DA_VPS

Confira se o arquivo chegou:

ls -lh /root/supabase_dump.sql

10. Restaurar o banco no PostgreSQL do Coolify

Liste os containers:

docker ps

Procure o container do PostgreSQL. Exemplo:

portal-postgres

Copie o dump para dentro do container:

docker cp /root/supabase_dump.sql portal-postgres:/tmp/supabase_dump.sql

Restaure:

docker exec -it portal-postgres psql -U portal_user -d portal -f /tmp/supabase_dump.sql

Teste se as tabelas apareceram:

docker exec -it portal-postgres psql -U portal_user -d portal -c "\dt"

Se quiser contar registros de uma tabela:

docker exec -it portal-postgres psql -U portal_user -d portal -c "select count(*) from noticias;"

11. Resolver extensões PostgreSQL

Alguns projetos Supabase usam extensões. Antes ou depois da restauração, talvez seja necessário ativar:

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE EXTENSION IF NOT EXISTS citext;

Se você usa busca vetorial:

CREATE EXTENSION IF NOT EXISTS vector;

Mas vector exige imagem PostgreSQL com pgvector instalado. Para portal de notícias comum, provavelmente não precisa.


12. Configurar o backend Dart Shelf

Use variáveis de ambiente para o banco:

APP_ENV=production
APP_PORT=8080

DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=portal
DB_USERNAME=portal_user
DB_PASSWORD=SENHA_FORTE

No Docker Compose, o host do banco será o nome do serviço:

postgres

Exemplo de conexão na aplicação:

postgres://portal_user:SENHA_FORTE@postgres:5432/portal

Dockerfile do backend Dart

Ajuste bin/server.dart para o nome real do arquivo principal da sua API.

FROM dart:stable AS build

WORKDIR /app
COPY pubspec.* ./
RUN dart pub get

COPY . .
RUN dart compile exe bin/server.dart -o bin/server

FROM debian:bookworm-slim

WORKDIR /app
COPY --from=build /runtime/ /
COPY --from=build /app/bin/server /app/server

EXPOSE 8080

CMD ["/app/server"]

Se a API precisa de arquivos estáticos, migrations ou templates, copie também:

COPY --from=build /app/migrations /app/migrations
COPY --from=build /app/config /app/config

13. Configurar o frontend NgDart 8

O NgDart 8 deve ser compilado para arquivos estáticos.

Exemplo:

webdev build --release

ou:

dart run build_runner build --release

Depois, sirva o resultado com Nginx.

Dockerfile do frontend

FROM nginx:alpine

COPY build/web /usr/share/nginx/html

EXPOSE 80

Se a pasta final do build for diferente, ajuste o caminho.


14. Docker Compose completo sugerido

Estrutura sugerida do repositório:

portal-noticias/
├── docker-compose.yml
├── .env
├── backend/
│   ├── Dockerfile
│   ├── pubspec.yaml
│   └── bin/server.dart
└── frontend/
    ├── Dockerfile
    ├── pubspec.yaml
    └── web/

docker-compose.yml:

services:
  postgres:
    image: postgres:16
    container_name: portal-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: portal
      POSTGRES_USER: portal_user
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - portal_postgres_data:/var/lib/postgresql/data
    networks:
      - portal_net

  api:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: portal-api
    restart: unless-stopped
    environment:
      APP_ENV: production
      APP_PORT: 8080
      DB_CONNECTION: pgsql
      DB_HOST: postgres
      DB_PORT: 5432
      DB_DATABASE: portal
      DB_USERNAME: portal_user
      DB_PASSWORD: ${POSTGRES_PASSWORD}
    depends_on:
      - postgres
    networks:
      - portal_net

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: portal-frontend
    restart: unless-stopped
    depends_on:
      - api
    networks:
      - portal_net

volumes:
  portal_postgres_data:

networks:
  portal_net:

Arquivo .env:

POSTGRES_PASSWORD=troque_esta_senha_por_uma_senha_muito_forte

No Coolify, cadastre o repositório Git e informe que o deploy usa Docker Compose.


15. Configurar domínios no Coolify

Configuração inicial mais simples:

frontend → seudominio.com.br
api      → api.seudominio.com.br

No frontend, configure a URL da API:

API_BASE_URL=https://api.seudominio.com.br

Se quiser evitar CORS no futuro, configure proxy para:

https://seudominio.com.br/api

Mas para a primeira migração, subdomínio api é mais simples de debugar.


16. Migrar imagens e arquivos do Supabase Storage

Se você usa Supabase Storage para imagens das notícias, precisa migrar os arquivos separadamente.

Opções:

Opção A: salvar imagens no disco da VPS
Opção B: usar Cloudflare R2
Opção C: usar MinIO no próprio VPS

Recomendação: Cloudflare R2.

Motivo:

  • Imagens crescem rápido.
  • Backup de mídia no disco da VPS pode ficar pesado.
  • R2 é compatível com API S3.
  • Você pode salvar no banco apenas o caminho/URL da imagem.

Fluxo recomendado:

Upload da notícia
  ↓
Backend Dart
  ↓
Cloudflare R2
  ↓
PostgreSQL salva URL/path

Migração básica:

  1. Baixar arquivos dos buckets do Supabase.
  2. Subir arquivos para R2 ou outro storage.
  3. Atualizar URLs no banco.
  4. Testar imagens antigas no portal novo.

17. Testes antes de virar o DNS

Na VPS:

docker ps

Logs da API:

docker logs -f portal-api

Teste local da API:

curl http://localhost:8080/health

Teste pelo domínio:

curl https://api.seudominio.com.br/health

Teste o banco:

docker exec -it portal-postgres psql -U portal_user -d portal -c "\dt"

Teste contagem de notícias:

docker exec -it portal-postgres psql -U portal_user -d portal -c "select count(*) from noticias;"

Checklist funcional:

  • Página inicial abre.
  • API responde /health.
  • Listagem de notícias funciona.
  • Cadastro de notícia funciona.
  • Edição funciona.
  • Upload de imagem funciona.
  • URLs públicas das imagens funcionam.
  • Login/admin funciona, se existir.
  • HTTPS está válido.
  • Logs não mostram erro recorrente.

18. Estratégia segura de virada

Não desligue o Supabase imediatamente.

Faça assim:

  1. Suba tudo no VPS.
  2. Restaure um dump de teste.
  3. Teste o portal no domínio provisório.
  4. Coloque o portal antigo em modo manutenção ou pause cadastros.
  5. Gere o dump final do Supabase.
  6. Restaure o dump final na VPS.
  7. Valide contagem de registros.
  8. Vire o DNS.
  9. Mantenha o Supabase ativo por alguns dias como fallback.
  10. Só cancele Supabase depois de confirmar que não há dados faltando.

19. Backup obrigatório

Configure backup diário do PostgreSQL.

Opções:

Coolify backup integrado
pg_dump manual via cron
backup para S3/R2
snapshot da VPS

Comando manual para backup:

mkdir -p /root/backups

docker exec portal-postgres pg_dump \
  -U portal_user \
  -d portal \
  --format=custom \
  --no-owner \
  --no-acl \
  > /root/backups/portal_$(date +%F).dump

Restaurar backup .dump:

docker exec -i portal-postgres pg_restore \
  -U portal_user \
  -d portal \
  --clean \
  --if-exists \
  --no-owner \
  --no-acl \
  < /root/backups/portal_2026-07-30.dump

Retenção sugerida:

Diário: 7 a 14 dias
Semanal: 4 semanas
Mensal: 3 a 6 meses

20. Segurança mínima da VPS

Atualize o sistema:

apt update && apt upgrade -y

Configure firewall básico:

ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable

Não exponha:

PostgreSQL 5432
Docker socket
Painel administrativo sem senha forte

Use senhas fortes e guarde em local seguro:

Senha root/SSH
Senha do Coolify
POSTGRES_PASSWORD
Chaves do storage R2/S3

21. Pontos de atenção do Supabase

Antes de migrar, confirme se o projeto usa:

Supabase Auth
Supabase Storage
Supabase Realtime
Edge Functions
RLS policies
Triggers
Functions SQL
Schemas diferentes de public
Extensões PostgreSQL

Se usa apenas tabelas comuns, a migração é simples.

Se usa auth.users, você precisa decidir:

1. Migrar usuários para autenticação própria
2. Continuar usando Supabase Auth temporariamente
3. Criar tabela própria de usuários/admins

Se usa RLS policies, teste bem. Em um backend próprio, normalmente a regra de autorização fica no backend Dart, e não diretamente no PostgreSQL.


22. Plano de execução comigo depois

Quando for fazer a migração real, envie:

1. Lista de tabelas do Supabase
2. Se usa Supabase Auth
3. Se usa Supabase Storage
4. Se usa RLS
5. Tamanho aproximado do banco
6. Quantidade/tamanho das imagens
7. Estrutura do backend Dart
8. Como o eloquent.dart lê as variáveis de conexão

Com isso, a ordem prática será:

1. Preparar VPS
2. Acessar Coolify
3. Criar PostgreSQL
4. Exportar Supabase
5. Restaurar dump
6. Subir backend Dart
7. Subir frontend NgDart
8. Testar rotas
9. Migrar imagens
10. Virar DNS

23. Fontes consultadas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment