Migre da Railway pro seu próprio servidor
Siga isso no seu próprio servidor. Comece grátis, sem cartão
A Railway é conveniente até o momento em que a fatura fica maior do que esperado, ou você quer um servidor no qual você possa entrar via SSH em vez de um painel entre você e a máquina. Sair dela significa dois trabalhos separados: fazer o código e a configuração da sua aplicação rodarem em outro lugar, e tirar os dados do Postgres gerenciado da Railway sem perder nada ou deixar a aplicação fora do ar por mais tempo do que uma mudança de DNS precisa. Nenhum dos dois é específico da Railway depois que você vê o formato disso: baixe a configuração, faça o dump do banco de dados, monte a mesma coisa num servidor que você controla, depois troque o DNS.
Este guia faz isso manualmente primeiro, com a sua própria VPS e Docker Compose, e depois cobre a importação em um clique que lê um projeto Railway direto e automatiza a maior parte disso.
O que você vai precisar
- Uma VPS. Um Hetzner CX23 (2 vCPU, 4 GB de RAM, atualmente €5,99/mês sem IVA) ou um droplet de 2 GB da DigitalOcean (US$12/mês) é suficiente pra maioria das aplicações pequenas mais o banco de dados Postgres delas.
- Uma conta Railway com um token de API, e acesso ao CLI (
npm install -g @railway/cli, depoisrailway login). - O DNS do seu domínio, pronto pra reapontar quando o servidor novo responder.
pg_dumpepg_restoreinstalados localmente, ou rode eles de dentro de um container (os dois vêm com a imagem Docker oficialpostgres).
Passo 1: descubra o que realmente está rodando
Antes de exportar qualquer coisa, conecte o CLI ao projeto pra você conseguir ver os serviços dele e as variáveis exatas que cada um tem:
railway login
railway link
railway variable list
Isso imprime toda variável que a Railway definiu pro serviço conectado, incluindo as que ela mesma gera como DATABASE_URL, RAILWAY_PRIVATE_DOMAIN e qualquer coisa que você adicionou você mesmo. Copie essa saída pra algum lugar, já que esse é o jeito mais rápido de ter toda chave de API, feature flag e secret num lugar só sem redigitar eles a partir do painel.
Passo 2: dê um endereço público ao banco de dados
O plugin de Postgres da Railway só escuta na rede privada da Railway por padrão; DATABASE_URL aponta pra um hostname .railway.internal que a sua própria máquina não consegue alcançar. Pra fazer o dump dele de fora da Railway, abra Settings → Networking do serviço de Postgres e ative Public Networking, que cria um proxy TCP e uma variável nova DATABASE_PUBLIC_URL ao lado da privada. Liste as variáveis de novo pra pegar ela:
railway variable list
A Railway cobra egress de rede pelo tráfego através desse proxy, então isso é um custo único pra migração, não algo pra deixar ligado depois a não ser que você precise dele pra outra coisa.
Passo 3: faça o dump do banco de dados
Com DATABASE_PUBLIC_URL em mãos, faça o dump dele a partir da sua própria máquina (ou do servidor novo, o que evita baixar os dados duas vezes):
pg_dump "$DATABASE_PUBLIC_URL" --format=custom --file=railway-export.dump
O formato customizado (-Fc) compacta o dump e deixa o pg_restore paralelizar a restauração depois, o que importa quando o banco de dados passa de algumas centenas de megabytes. Mantenha a Railway rodando enquanto faz isso; nada num dump toca no banco de dados em produção, então ainda não existe downtime.
Passo 4: configure o servidor novo
Instale o Docker na VPS do mesmo jeito independente do provedor:
curl -fsSL https://get.docker.com | sh
apt-get install -y docker-compose-plugin
Escreva um docker-compose.yml com Postgres, a sua aplicação, e Caddy pro HTTPS:
services:
db:
image: postgres:18
restart: unless-stopped
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myapp
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
app:
build: .
restart: unless-stopped
env_file: .env
depends_on:
- db
expose:
- "3000"
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
depends_on:
- app
volumes:
postgres_data:
caddy_data:
caddy_config:
Se a Railway já estava fazendo build da sua aplicação a partir de um Dockerfile, reuse ele como está; a etapa de build não sabe nem se importa com qual plataforma roda a imagem resultante. Se a Railway estava fazendo build sem um (através da própria detecção do Railpack/Nixpacks dela), você vai precisar escrever um agora. O conteúdo exato depende da sua linguagem, mas o padrão é o mesmo coberto em Deploy from a repository: instalar dependências, fazer build, e rodar o comando de start que a sua aplicação já usa em desenvolvimento.
Passo 5: restaure o banco de dados
Suba só o banco de dados primeiro, depois restaure dentro dele:
docker compose up -d db
docker compose exec -T db pg_restore --no-owner --no-acl -U myapp -d myapp < railway-export.dump
--no-owner e --no-acl pulam declarações de role e permissão específicas da Railway que não existem neste servidor, já que o dump foi feito a partir de um banco de dados pertencente a um role diferente do que você acabou de criar.
Passo 6: traga as variáveis de ambiente
Traduza a saída de railway variable list do passo 1 pra um arquivo .env pro serviço app. A maior parte copia sem mudar nada; alguns valores precisam apontar pra configuração nova em vez da da Railway:
DATABASE_URL=postgresql://myapp:changeme@db:5432/myapp
REDIS_URL=redis://redis:6379
# tudo mais de `railway variable list` que a sua aplicação lê diretamente
Se a aplicação usava o Redis da Railway ou outro add-on, adicione ele como o próprio serviço no docker-compose.yml do mesmo jeito que o db é definido, e aponte a URL correspondente pro nome desse serviço em vez de um hostname da Railway.
Passo 7: escreva o Caddyfile e vá ao ar
myapp.example.com {
reverse_proxy app:3000
}
Aponte o registro A do domínio pro servidor novo, espere ele resolver, depois inicie tudo:
docker compose up -d --build
docker compose logs -f caddy
Você deve ver o Caddy obter um certificado e a aplicação responder em https://myapp.example.com. Compare algumas páginas ou respostas de API contra o deploy da Railway que ainda está rodando antes de considerar isso concluído.
Passo 8: faça o cutover e desative a Railway
Depois que o servidor novo tiver servido tráfego real corretamente por um dia ou dois, aponte qualquer referência restante pra ele (webhooks, URLs de API de app mobile, qualquer coisa fixada no código), e desligue o projeto na Railway. Mantenha o arquivo de dump por algumas semanas antes de apagar.
O caminho mais rápido: importe da Railway direto
O Applications → Import da Railyard lê projetos Railway através de um token de API e monta um plano antes de qualquer coisa ser criada, em vez de adivinhar. De como a importação realmente funciona: ela lê os serviços, ambientes e variáveis de todo projeto através da própria API da Railway, e pra cada serviço ela traz o repositório Git ou a imagem exata que a Railway roda, o comando de start dela quando um está definido, os domínios customizados dela, e as variáveis dela, limpas das específicas da Railway. Ela também detecta quais variáveis apontam pra um add-on de Postgres, Redis, MongoDB ou MySQL a partir do formato das URLs deles, e oferece provisionar o banco de dados correspondente no servidor novo e copiar os dados pra ele automaticamente, a mesma abordagem de pg_dump-pra-um-banco-de-dados-novo de acima, sem você tocar num terminal.
Um banco de dados sem endereço público ainda precisa dessa mesma configuração Public Networking ativada na Railway primeiro. Nada consegue transmitir o conteúdo de um banco de dados sem o proxy TCP publicando ele em algum lugar acessível.
Comece a importação pela página de migração, cole um token de API da Railway, revise o plano que a Railyard mostra pra cada serviço, e escolha o servidor pra fazer deploy. Veja Import from another platform pro que vem automaticamente versus o que ainda precisa de um valor digitado manualmente (a API da Railway não expõe o conteúdo de secret files, por exemplo), e Hosting costs se o motivo todo dessa mudança é comparar o que você está realmente pagando contra um servidor que você provisiona você mesmo.
Resolução de problemas
O pg_dump falha ao conectar, ou trava. Confirme que o Public Networking de fato está ativado pro serviço de Postgres e que você copiou DATABASE_PUBLIC_URL, não DATABASE_URL; o privado resolve pra um hostname .railway.internal que a sua máquina não consegue alcançar de forma alguma, o que geralmente aparece como uma falha de resolução de DNS em vez de uma conexão recusada.
O pg_restore dá erro sobre roles ou permissões que não existem. Adicione --no-owner --no-acl ao comando de restauração, que descarta declarações de propriedade e grant vinculadas aos nomes de role internos da Railway.
A aplicação não consegue alcançar o banco de dados depois do cutover. Cheque se DATABASE_URL no .env novo aponta pro nome do serviço do Compose (db), não localhost ou o hostname antigo da Railway; dentro do Docker Compose, os serviços se alcançam pelo nome do serviço na rede compartilhada, não por 127.0.0.1.
O HTTPS nunca é emitido no servidor novo. O DNS do domínio quase certamente ainda está apontando pra Railway, ou ainda não se propagou. Cheque com dig myapp.example.com e compare contra o IP do servidor novo antes de depurar o próprio Caddy.
Falta de memória ao restaurar um dump grande numa VPS pequena. O pg_restore com as configurações padrão pode usar uma quantidade surpreendente de memória num dump com muitos índices. Restaure sem --jobs (serial, mais lento, menos memória) em qualquer coisa abaixo de 4 GB de RAM, ou aumente o servidor temporariamente pra restauração e reduza depois.
Perguntas frequentes
Isso funciona se eu não estiver usando Postgres? O mesmo formato se aplica ao MySQL (mysqldump/mysql) e ao Redis (redis-cli --rdb), embora os comandos exatos sejam diferentes. A importação da Railyard também detecta add-ons de Redis, MongoDB e MySQL do mesmo jeito que detecta o Postgres.
Eu posso migrar gradualmente, um serviço por vez? Sim. Nada exige mover um projeto Railway inteiro de uma vez; importe ou refaça um serviço, verifique, depois mova o próximo.
O que acontece com o armazenamento efêmero ou os volumes da Railway? Qualquer coisa escrita num volume da Railway precisa ser copiada separadamente, geralmente com scp ou rsync contra uma sessão de shell avulsa do lado da Railway antes de você desativar ela, já que nem a exportação de variável do CLI nem um dump de banco de dados tocam em volumes do sistema de arquivos.
A diferença de preço realmente vale a migração? Isso depende inteiramente do tamanho e do tráfego da aplicação. Hosting costs e o verificador de conta do Heroku são feitos pra tornar essa comparação concreta em vez de um palpite, mesmo que o segundo seja nomeado especificamente pro Heroku.
As partes que fazem isso parecer arriscado, principalmente o banco de dados, também são as mais mecânicas depois que você fez uma vez: dump, restauração, verificação, troca de DNS. Todo o resto é só rodar o mesmo container em algum lugar que não seja a Railway.
Por que escolhem o Railyard para isso
- Sua própria conta de cloud. Faça deploy no DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, ou em qualquer servidor Linux via SSH, cobrado direto pelo seu provedor, sem markup.
- Um preço fixo por equipe. O Free é grátis para sempre: um servidor mais um servidor de banco de dados, três apps, sem cartão. O Pro custa US$19 por mês para até cinco servidores e apps ilimitados. O Team custa US$49 por mês com servidores ilimitados. Sem cobrança por app, por deploy, por assento ou por gigabyte.
- Templates de um clique. Faça deploy de apps open source, como bancos de dados, analytics e ferramentas de dev, no seu próprio servidor em um clique.
- Bancos de dados gerenciados. Postgres, MySQL e Redis com backups programados, verificados por restauração, e recuperação point-in-time.
- Push-to-deploy. Faça deploy a partir do GitHub, GitLab ou da CLI, com HTTPS automático e releases sem downtime.