Moverte de Railway a tu propio servidor
Sigue esto en tu propio servidor. Empieza gratis, sin tarjeta
Railway es cómodo hasta el día en que la factura es más alta de lo esperado, o cuando quieres un servidor al que puedas entrar por SSH en vez de un panel entre tú y la máquina. Dejarlo significa dos tareas separadas: hacer que el código y la configuración de tu app corran en otro lado, y sacar los datos de la base Postgres administrada por Railway sin perder nada y sin dejar la app caída más tiempo del que toma un cambio de DNS. Ninguna de las dos es específica de Railway una vez que ves la forma general: sacar la configuración, hacer un dump de la base, levantar lo mismo en un servidor que controlas tú, y después cambiar el DNS.
Esta guía lo hace primero a mano, con tu propio servidor y Docker Compose, y después cubre el atajo de un clic que lee un proyecto de Railway directamente y automatiza casi todo esto.
Lo que necesitas
- Un servidor VPS. Un Hetzner CX23 (2 vCPU, 4 GB de RAM, actualmente €5.99/mes sin IVA) o un droplet de DigitalOcean de 2 GB ($12 USD/mes) alcanza para la mayoría de las apps pequeñas junto con su base Postgres.
- Una cuenta de Railway con un token de API, y acceso al CLI (
npm install -g @railway/cli, luegorailway login). - El DNS de tu dominio, listo para redirigirlo una vez que el nuevo servidor responda.
pg_dumpypg_restoreinstalados localmente, o ejecutados desde un contenedor (ambos vienen incluidos en la imagen Docker oficial depostgres).
Paso 1: averiguar qué está corriendo en realidad
Antes de exportar nada, vincula el CLI al proyecto para ver sus servicios y las variables exactas de cada uno:
railway login
railway link
railway variable list
Eso imprime cada variable que Railway configuró para el servicio vinculado, incluyendo las que genera por sí mismo como DATABASE_URL, RAILWAY_PRIVATE_DOMAIN, y cualquiera que hayas agregado tú. Copia esa salida en algún lado, porque es la forma más rápida de obtener cada API key, feature flag y secreto en un solo lugar sin volver a escribirlos desde el panel.
Paso 2: darle a la base de datos una dirección pública
El plugin de Postgres de Railway solo escucha en la red privada de Railway por defecto; DATABASE_URL apunta a un host .railway.internal que tu máquina no puede alcanzar. Para hacerle un dump desde fuera de Railway, abre Settings → Networking del servicio Postgres y activa Public Networking, lo que crea un proxy TCP y una nueva variable DATABASE_PUBLIC_URL junto a la privada. Lista las variables de nuevo para obtenerla:
railway variable list
Railway cobra por el tráfico de salida que pasa por este proxy, así que es un costo único para la migración, no algo para dejar activado después a menos que lo necesites para otra cosa.
Paso 3: hacer el dump de la base de datos
Con DATABASE_PUBLIC_URL en mano, haz el dump desde tu propia máquina (o desde el nuevo servidor, lo que evita traer los datos dos veces):
pg_dump "$DATABASE_PUBLIC_URL" --format=custom --file=railway-export.dump
El formato custom (-Fc) comprime el dump y le permite a pg_restore paralelizar la restauración después, algo que importa en cuanto la base pasa de unos cientos de megabytes. Mantén Railway corriendo mientras haces esto; un dump no toca para nada la base en vivo, así que todavía no hay tiempo de inactividad.
Paso 4: preparar el nuevo servidor
Instala Docker en el VPS de la misma forma sin importar el proveedor:
curl -fsSL https://get.docker.com | sh
apt-get install -y docker-compose-plugin
Escribe un docker-compose.yml con Postgres, tu app, y Caddy para el 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:
Si Railway ya construía tu app a partir de un Dockerfile, reutilízalo tal cual; el paso de build no sabe, ni necesita saber, qué plataforma corre la imagen resultante. Si Railway la construía sin Dockerfile (con su propia detección Railpack/Nixpacks), vas a tener que escribir uno ahora. El contenido exacto depende de tu lenguaje, pero el patrón es el mismo que se cubre en Desplegar desde un repositorio: instalar dependencias, hacer el build, y correr el comando de arranque que tu app ya usa en desarrollo.
Paso 5: restaurar la base de datos
Levanta primero solo la base, y luego restaura los datos en ella:
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 y --no-acl se saltan las instrucciones de roles y permisos propias de Railway que no existen en este servidor, ya que el dump se hizo desde una base que pertenecía a un rol distinto al que acabas de crear.
Paso 6: trasladar las variables de entorno
Traduce la salida de railway variable list del paso 1 a un archivo .env para el servicio app. La mayoría se copia sin cambios; unos pocos valores deben apuntar a la nueva configuración en vez de a la de Railway:
DATABASE_URL=postgresql://myapp:changeme@db:5432/myapp
REDIS_URL=redis://redis:6379
# todo lo demás de `railway variable list` que tu app lee directamente
Si la app usaba Redis u otro add-on de Railway, agrégalo como su propio servicio en docker-compose.yml, igual que db, y apunta la URL correspondiente al nombre de ese servicio en vez de a un host de Railway.
Paso 7: escribir el Caddyfile y salir a producción
myapp.example.com {
reverse_proxy app:3000
}
Apunta el registro A del dominio al nuevo servidor, espera a que se propague, y arranca todo:
docker compose up -d --build
docker compose logs -f caddy
Deberías ver que Caddy obtiene un certificado y que la app responde en https://myapp.example.com. Compara algunas páginas o respuestas de la API contra el despliegue de Railway que todavía sigue corriendo antes de dar esto por terminado.
Paso 8: hacer el corte y dar de baja Railway
Una vez que el nuevo servidor lleve sirviendo tráfico real correctamente durante uno o dos días, redirige cualquier referencia que quede (webhooks, URLs de API de una app móvil, cualquier cosa escrita a mano) y apaga el proyecto de Railway. Guarda el archivo de dump unas semanas antes de borrarlo.
El atajo: importar desde Railway directamente
Applications → Import de Railyard lee proyectos de Railway a través de un token de API y arma un plan antes de crear nada, en vez de adivinar. Según cómo funciona realmente el import: lee los servicios, entornos y variables de cada proyecto a través de la propia API de Railway, y para cada servicio trae el repositorio Git o la imagen exacta que corre Railway, su comando de arranque cuando está definido, sus dominios personalizados, y sus variables, limpias de las que son propias de Railway. También detecta qué variables apuntan a un add-on de Postgres, Redis, MongoDB o MySQL según la forma de sus URLs, y ofrece aprovisionar la base correspondiente en el nuevo servidor y copiar los datos automáticamente, el mismo enfoque de dump y restauración en una base nueva que se describe arriba, sin que toques una terminal.
Una base de datos sin dirección pública sigue necesitando que se active primero ese mismo interruptor de Public Networking del lado de Railway. Nada puede transmitir el contenido de una base sin que el proxy TCP la publique en algún lugar alcanzable.
Empieza el import desde la página de migración, pega un token de API de Railway, revisa el plan que Railyard muestra para cada servicio, y elige el servidor donde desplegarlo. Mira Importar desde otra plataforma para ver qué se trae automáticamente frente a lo que todavía hay que escribir a mano (la API de Railway no expone el contenido de archivos secretos, por ejemplo), y Costos de hosting si toda la razón de esta mudanza es comparar lo que realmente estás pagando contra un servidor que aprovisionas tú mismo.
Solución de problemas
pg_dump no logra conectar, o se queda colgado. Confirma que Public Networking esté realmente activado para el servicio Postgres y que copiaste DATABASE_PUBLIC_URL, no DATABASE_URL; la privada apunta a un host .railway.internal que tu máquina no puede alcanzar de ninguna forma, lo que normalmente se ve como un fallo de resolución DNS en vez de una conexión rechazada.
pg_restore da errores sobre roles o permisos que no existen. Agrega --no-owner --no-acl al comando de restauración, que omite las instrucciones de propiedad y permisos ligadas a los nombres de roles internos de Railway.
La app no puede alcanzar la base después del corte. Verifica que DATABASE_URL en el nuevo .env apunte al nombre del servicio de Compose (db), no a localhost ni al viejo host de Railway; dentro de Docker Compose, los servicios se alcanzan entre sí por nombre de servicio en la red compartida, no por 127.0.0.1.
El HTTPS nunca se emite en el nuevo servidor. El DNS del dominio casi seguro todavía apunta a Railway, o no se ha propagado aún. Verifica con dig myapp.example.com y compáralo con la IP del nuevo servidor antes de buscar el problema en Caddy.
Se queda sin memoria al restaurar un dump grande en un VPS pequeño. pg_restore con la configuración por defecto puede usar una cantidad sorprendente de memoria en un dump con muchos índices. Restaura sin --jobs (en serie, más lento, menos memoria) en cualquier cosa con menos de 4 GB de RAM, o agranda el servidor temporalmente para la restauración y baja de tamaño después.
Preguntas frecuentes
¿Esto funciona si no uso Postgres? El mismo patrón aplica a MySQL (mysqldump/mysql) y a Redis (redis-cli --rdb), aunque los comandos exactos cambian. El import de Railyard también detecta add-ons de Redis, MongoDB y MySQL de la misma forma que detecta Postgres.
¿Puedo migrar de a poco, un servicio a la vez? Sí. Nada obliga a mover un proyecto entero de Railway de una sola vez; importa o reconstruye un servicio, verifícalo, y luego sigue con el siguiente.
¿Qué pasa con el almacenamiento efímero o los volúmenes de Railway? Todo lo que se haya escrito en un volumen de Railway hay que copiarlo por separado, normalmente con scp o rsync desde una sesión de shell puntual del lado de Railway antes de dar de baja el proyecto, ya que ni la exportación de variables del CLI ni un dump de base de datos tocan los volúmenes del sistema de archivos.
¿La diferencia de precio realmente justifica la migración? Eso depende por completo del tamaño y el tráfico de la app. Costos de hosting y el verificador de factura de Heroku están hechos para volver esa comparación concreta en vez de una suposición, aunque el segundo lleve el nombre de Heroku específicamente.
Las partes que hacen que esto parezca arriesgado, sobre todo la base de datos, son también las más mecánicas una vez que lo haces una vez: dump, restauración, verificación, cambio de DNS. Todo lo demás es simplemente correr el mismo contenedor en otro lugar que no sea Railway.
Por qué eligen Railyard para esto
- Tu propia cuenta en la nube. Despliega en DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, o cualquier servidor Linux por SSH, facturado directamente por tu proveedor, sin márgenes adicionales.
- Un precio fijo por equipo. El plan Free es gratis para siempre: un servidor más un servidor de base de datos, tres apps, sin tarjeta. Pro cuesta $19 al mes por hasta cinco servidores y apps ilimitadas. Team cuesta $49 al mes con servidores ilimitados. Sin cargos por app, por despliegue, por asiento ni por gigabyte.
- Plantillas de un clic. Despliega apps de código abierto, como bases de datos, analítica y herramientas de desarrollo, en tu propio servidor con un clic.
- Bases de datos administradas. Postgres, MySQL y Redis, con copias de seguridad programadas y verificadas por restauración, y recuperación a un punto en el tiempo.
- Despliegue con git push. Despliega desde GitHub, GitLab o la CLI, con HTTPS automático y lanzamientos sin tiempo de inactividad.