slew docs/start/migrate-from-netlify

Migrate from Netlify

Netlify's core loop — repo connected, push to deploy, deploy previews per PR — is slew's core loop too, and Netlify's config translates unusually well: _redirects splats are almost exactly slew's redirect syntax. A migration is: connect the git repo, copy env vars, translate the config, point DNS. The importer does the first three in one command, and nothing on Netlify changes until you flip DNS yourself.

The short version

cd your-site
slew login
slew import netlify --dry-run   # compat report + plan, creates nothing
slew import netlify             # do it

(In a directory with Netlify traces, bare slew import figures out the platform by itself.)

The importer reads netlify.toml, _redirects (repo root, public/ or static/) and package.json. With a Netlify token (--token or NETLIFY_AUTH_TOKEN, in a directory linked with netlify link) it also pulls build settings, env vars, the connected repo and domains from the Netlify API — strictly read-only. It then:

  1. Prints a compatibility report — every gap stated before anything is created.
  2. Creates the slew project and links the directory.
  3. Copies env vars: API values for the production context (scope both), plus [build.environment] from netlify.toml (scope build). Secret values — which Netlify never returns decrypted — are listed by name to set by hand.
  4. Translates redirects from both netlify.toml and _redirects into slew's redirect rules.
  5. Connects the GitHub repo with your build command and publish directory — every push builds and deploys, PRs get preview URLs.
  6. Prints the DNS cutover checklist, filled in with your custom domain and aliases.

What maps to what

On Netlify On slew
git push → deploy Same — git push-to-deploy via the GitHub App
Deploy previews per PR Same — expiring preview URLs, commented on the PR
Branch deploys / branch subdomains Branch domains: slew domains add staging.example.com --branch staging
Environment variables slew env — encrypted at rest, runtime/build/both scopes
_redirects / [[redirects]] slew.json redirects and rewrites — splats, :splat and status codes carry over verbatim
SPA fallback (/* /index.html 200) Default behavior — nothing to configure
Instant rollback slew rollback <deployment-id> — re-points, no rebuild
Scheduled functions slew cron — scheduled HTTP calls into your app (5 per project)
Netlify Functions / Edge Functions No equivalent runtime — see below
Netlify Forms No equivalent — see below

What's genuinely different

  • No Functions runtime. slew runs your app as one long-running Node 22 server. /.netlify/functions/* endpoints would 404 — port them into the app itself (Next.js API routes, an Express server, any framework's server routes). For a static site with a couple of form-handler functions this is the main porting work; the importer warns when it sees a functions directory. The site itself deploys fine either way.
  • No Netlify Forms. Form submissions with data-netlify attributes are a Netlify platform feature. Use any form backend, or a route in your own app. The importer can't detect form usage — check your templates before cutting over.
  • Same-site 200 rules become slew rewrites; proxies don't. A rule like /app/* /app/index.html 200 translates as-is. A 200 rule pointing at another origin is a proxy — slew doesn't do those; handle it in your app. The one 200-rule almost every SPA has (/* /index.html 200) is slew's default behavior and is skipped with exactly that note.
  • Custom headers (_headers, [[headers]]) are not applied by the platform. Next.js and other server apps set them in framework config or code — that works on slew because the app is a real server. Static sites have no equivalent yet.
  • Build plugins are ignored. Builds run your plain install and build commands in a clean container; most plugins (sitemaps, image transforms) have framework-level equivalents.
  • One region. Rendering happens in Germany behind a CDN. Edge Functions have no equivalent — that logic lives in your server.

Per-framework notes

The same recipes as everywhere on slew: Next.js needs output: 'standalone' (page); SvelteKit on @sveltejs/adapter-netlify swaps to @sveltejs/adapter-node or adapter-static (page); Astro, Remix and static builds carry over as-is. Server apps built with pnpm/yarn/bun need a committed package-lock.json for the builder's npm ci (npm i --package-lock-only) — the importer warns when this applies.

Cutting over DNS

Identical to the Vercel cutover, and the importer prints it with your real domains: verify the deploy at https://<project>.slew.cloud, lower the TTL to 300s, slew domains add each domain and point DNS at the printed CNAME target (apex domains via ALIAS/ANAME), re-run for TLS, confirm with slew domains cert, then remove the domain from the Netlify site. Nothing on Netlify changed until that last step — rollback is pointing DNS back.

Stuck anywhere? Email support — a first migration is exactly the kind of thing worth doing together over a screen share.