Migrar desde Railway
Los conceptos se corresponden uno a uno, la mayoría de los documentos de la API funcionan sin cambios y los comandos de la CLI tienen los mismos nombres. Qué cambiar, en orden.
Correspondencia de conceptos#
| Railway | Skiffly | Notas |
|---|---|---|
| Espacio de trabajo / equipo | Espacio de trabajo | roles owner, admin, member; sin grupos de acceso |
| Proyecto → Entorno → Servicio | igual | los entornos de PR son pr-<n> y se activan por proyecto |
| Origen del servicio: repositorio, imagen | igual | registries privados aún no soportados |
| Railpack / Nixpacks / Dockerfile | Railpack / Dockerfile | los proyectos con Nixpacks se compilan con Railpack o con un Dockerfile |
${{Postgres.DATABASE_URL}}, ${{shared.VAR}} | misma sintaxis | |
RAILWAY_PUBLIC_DOMAIN, RAILWAY_PRIVATE_DOMAIN, … | SKIFFLY_PUBLIC_DOMAIN, SKIFFLY_PRIVATE_DOMAIN, … | ver variables integradas |
Red privada service.railway.internal | <service-slug> (http://api:8080) | IPv4, solo dentro del mismo entorno |
Dominio generado *.up.railway.app | *.skiffly.cloud | |
| Dominio propio: CNAME + verificación | CNAME a edge.skiffly.cloud + TXT _skiffly.<host> | |
| Proxy TCP | Proxy TCP (edge.skiffly.cloud:2xxxx) | skiffly proxy, skiffly connect |
| Volúmenes | Volúmenes | una réplica por servicio con volumen; respaldos diarios |
| Programación cron | Programación cron | |
| App Sleeping | App Sleeping | ~10 min de inactividad |
| Ruta del health check | Ruta del health check | hasta 5 min de arranque |
| Réplicas, regiones | Réplicas | hoy solo UE |
| Cambios preparados → Desplegar | no existen | los cambios se aplican de inmediato; vuelve a desplegar cuando haga falta |
| Facturación por uso, factura | Facturación por uso, saldo prepago | PayPal o cripto; 3 días de gracia y luego pausa |
CLI railway | CLI skiffly | mismos nombres de comando cuando la función existe |
| Servidor MCP de Railway | Servidor MCP de Skiffly | mismos nombres de herramienta cuando la función existe |
railway/iac (defineRailway) | @skiffly/config (defineSkiffly) | buscar y reemplazar |
| Plantillas | Plantillas | 136 en el catálogo, templateDeployV2 |
| Observabilidad: logs, métricas | Logs (búfer de 5 000 líneas), métricas (7 días) | todavía sin log drains |
| Webhooks, funciones, buckets, sandboxes | no disponibles |
Importar con la CLI: skiffly import --from-railway#
La CLI (o la herramienta MCP import_from_railway) lee tu proyecto de Railway a través de la API pública de Railway y lo
recrea en Skiffly de una sola vez, así que los pasos 2, 4, 5 y la mayor parte del 6 de abajo ocurren automáticamente. Necesitas
un token de cuenta de Railway (un token de espacio de trabajo también sirve); solo se usa
para leer y no se guarda.
skiffly login
skiffly import --from-railway --dry-run # elige el proyecto de Railway, previsualiza el plan
skiffly import --from-railway # aplicar: crea un proyecto nuevo en Skiffly (o --project <id|name>)Flags: --railway-token <t> (o RAILWAY_TOKEN), --railway-project <id> (el id de la URL del panel de Railway o de
railway status), --project <id|name|new> y --workspace <w> para el destino en Skiffly, --environment <name> para
importar un solo entorno, --dry-run, --yes, --json.
Qué se importa, por entorno (los entornos de PR se omiten):
| Railway | Skiffly |
|---|---|
| Entornos | Entornos con el mismo nombre (production se corresponde con el de producción del proyecto) |
| Servicios desde un repositorio de GitHub: rama, directorio raíz, ruta del Dockerfile, comando de build | serviceCreate con el mismo origen y builder (RAILPACK, o DOCKERFILE cuando hay una ruta de Dockerfile definida); los builds con Nixpacks/Heroku/Paketo pasan a Railpack |
| Servicios desde una imagen Docker | Servicio de imagen; el puerto se deduce de PORT o de la imagen (Postgres 5432, Redis 6379, …) |
| Comando de inicio, health check, programación cron, réplicas, app sleeping | serviceInstanceUpdate por entorno |
| Variables (por servicio y compartidas), sin renderizar | Se copian conservando las referencias ${{Service.VAR}} y ${{shared.VAR}}; RAILWAY_PUBLIC_DOMAIN, RAILWAY_PRIVATE_DOMAIN, RAILWAY_ENVIRONMENT, … dentro de las referencias pasan a SKIFFLY_*; las variables RAILWAY_* en sí no se copian |
| Volúmenes | Se crean vacíos en la misma ruta de montaje (tamaño redondeado hacia arriba a GB enteros) |
| Dominios propios | Se adjuntan al servicio; se imprimen el CNAME y el registro TXT _skiffly |
| Proxies TCP | Se recrean en edge.skiffly.cloud con un puerto nuevo |
No se importa: el contenido de volúmenes y bases de datos, los dominios *.up.railway.app generados por Railway, los comandos
pre-deploy, el historial de despliegues, las credenciales de registries privados, railway.json/railway.toml (se usan en
su lugar los valores de la instancia del servicio). Los servicios que ya existen por nombre en el proyecto destino se dejan
intactos. No se despliega nada: los servicios se crean con los despliegues desactivados para que puedas revisar las
variables primero.
El comando termina con una tabla de lo creado y una lista Hazlo a mano, normalmente:
- copiar los datos de bases de datos/volúmenes (
pg_dumpen Railway →skiffly connect <service>/skiffly sshpara restaurar; ver el paso 3); - crear los registros DNS de cada dominio propio (
skiffly domain status <domain>los muestra) y luego quitar el dominio de Railway; - actualizar los clientes de los proxies TCP al nuevo host:puerto (
skiffly proxy list) y las variables que referenciabanRAILWAY_TCP_PROXY_*; - definir cualquier variable que referenciara una variable integrada exclusiva de Railway; mover los comandos pre-deploy al comando de inicio;
skiffly link <projectId>y luegoskiffly up/skiffly redeploy -s <service>por servicio, manteniendo Railway en marcha hasta que Skiffly esté saludable.
Desde un agente de IA: la herramienta import_from_railway del servidor MCP de Skiffly acepta los mismos
parámetros (railwayToken, railwayProjectId, projectId o createProjectName, environment, dryRun) y
devuelve el mismo informe.
Paso a paso#
-
Inicia sesión en app.skiffly.dev con GitHub, instala la GitHub App para los repositorios privados y recarga el saldo.
-
Exporta las variables de Railway:
railway variables --kv > vars.envpor servicio. Elimina las entradasRAILWAY_*; reemplaza las referencias a variables provistas por Railway con los equivalentesSKIFFLY_*. -
Primero las bases de datos. Despliega la plantilla (
skiffly deploy --template postgres) y luego mueve los datos a través de un proxy TCP:# Lado de Railway pg_dump "$RAILWAY_DATABASE_URL" -Fc -f db.dump # Lado de Skiffly skiffly connect postgres --print # imprime la URL; crea el proxy en el primer uso pg_restore -d "<url from --print>" --no-owner --clean --if-exists db.dump skiffly proxy delete <port> # cierra el puerto público al terminarRedis y Mongo:
redis-cli --rdb/mongodumpy la restauración correspondiente a través deskiffly connect. -
Servicios.
skiffly initen cada repositorio y luegoskiffly up. Define las variables:skiffly variables set --skip-deploys $(grep -v '^RAILWAY_' vars.env | xargs) skiffly variables set 'DATABASE_URL=${{Postgres.DATABASE_URL}}' skiffly redeployO describe todo una sola vez en
.skiffly/skiffly.tsy ejecutaskiffly config apply; unrailway.tsde Railway se porta condefineRailway→defineSkiffly. -
La configuración que Railway guardaba en
railway.json/railway.toml(comando de build, comando de inicio, health check, política de reinicio, cron, patrones de watch) va en el servicio:update-serviceen MCP,serviceInstanceUpdateen GraphQL o el archivo de configuración. Skiffly no leerailway.json. -
Dominios. Genera un dominio
skiffly.cloudpara probar, luego agrega el dominio propio y cambia el CNAME cuando se emita el certificado. Mantén el servicio de Railway en marcha hasta que el DNS se haya propagado. -
Clientes de la API. Cambia el endpoint a
https://api.skiffly.dev/graphqly el token. Los documentos que usanprojects,deployments(input:),variables,serviceInstanceUpdate,serviceInstanceDeployV2,customDomainCreate,templateDeployV2y los demás campos compatibles funcionan tal cual;buildLogspasa a serdeploymentLogsfiltrado porstream. -
Automatización. Las GitHub Actions que ejecutaban
railway upejecutannpx skiffly up(oskiffly config apply --yes) conSKIFFLY_TOKEN.
Qué revisar antes de hacer el cambio#
- La app escucha en
$PORT(Skiffly lo define; 8080 por defecto si no configuras ninguno). - Nada depende de los hostnames
*.railway.internalni de la red privada IPv6. - El SMTP saliente por el puerto 25 está bloqueado; usa el 587 con autenticación o una API de correo.
- Límites del plan: Hobby permite 8 GiB / 8 vCPU por servicio, Pro 32 GiB / 32 vCPU. Free es un solo servicio.
- Entornos de vista previa: activa las vistas previas de PR por proyecto; los forks se ignoran.