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:
- Prints a compatibility report — every gap stated before anything is created.
- Creates the slew project and links the directory.
- Copies env vars: API values for the production context (scope
both), plus[build.environment]from netlify.toml (scopebuild). Secret values — which Netlify never returns decrypted — are listed by name to set by hand. - Translates redirects from both
netlify.tomland_redirectsinto slew's redirect rules. - Connects the GitHub repo with your build command and publish directory — every push builds and deploys, PRs get preview URLs.
- 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-netlifyattributes 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 200translates 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.