Migrating from NGINX to Apache APISIX: what the move involves
Effort depends on three things: how many server and location blocks you run, how much custom Lua or NGINX module logic sits in those files, and how tightly authentication is wired into the proxy layer. A small set of reverse proxy rules with basic auth and a few rate limits is a short translation exercise. A large estate with hand-written access_by_lua blocks, auth_request subrequests to internal services, and rewrites that depend on specific header behavior needs a longer inventory, and each of those pieces needs a named APISIX plugin or a deliberate decision to drop it. Ask any partner to base a quote on a written inventory of your nginx.conf includes, upstream blocks, Lua files and auth dependencies rather than on a rough sense of route count.
APISIX is built on NGINX and etcd, so the data plane will feel familiar: the same worker model, the same kind of HTTP proxying, and the same reliance on the OpenResty ecosystem underneath. What changes is where configuration lives and how it is applied. Instead of editing files and reloading, you define routes, upstreams, consumers and plugins through the Admin API or a declarative tool, and APISIX picks up changes dynamically. Because of that, the move is mostly a translation and control-plane change rather than a new traffic architecture. Your load balancers, TLS termination points and upstream services can usually stay where they are, which keeps the cutover narrow and the rollback path short.
Plan the work around that control-plane shift. Decide early whether you will run APISIX in traditional mode with etcd or in standalone mode driven by a YAML file, since that choice changes how you store, review and roll back config. Confirm with your own environment which Admin API port and access controls you will expose, and treat the migration as the point to review who can change routes in production.
Which NGINX capabilities map to APISIX and which need rework
This table compares common hand-written NGINX reverse proxy features with their APISIX equivalents and flags the items that need a rewrite rather than a direct translation. Read each row as a starting point for inventory: the migration note tells you what changes in behavior, configuration or operations. Most standard proxying maps cleanly, while custom modules and per-node counters need planning before cutover.
| NGINX capability | APISIX equivalent | Migration notes |
|---|---|---|
| server and location blocks | Routes matched on host, URI and methods, with priority settings | Each location becomes a route. Check regex locations and prefix precedence, since route priority in APISIX does not follow NGINX location ordering automatically. |
| upstream blocks and load balancing | Upstreams with nodes and balancing types, attached to routes | Confirm each balancing method and health check you rely on is available in the APISIX version you deploy. Upstream nodes are managed through the Admin API or configuration files. |
| TLS termination (ssl_certificate) | SSL objects with certificates and SNI matching | Certificate and key handling moves into APISIX configuration. Plan where private keys are stored and who can read them, because the Admin API accepts them. |
| proxy_set_header and add_header | proxy-rewrite plugin (request headers add, set, remove) and response-rewrite for responses | Header changes are declared per route. Verify that any header trust logic from the NGINX configuration, such as forwarded client IP handling, is reproduced explicitly. |
| rewrite and return directives | proxy-rewrite plugin for upstream URI, regex_uri and Host header changes | URI normalization stays on by default. Test paths containing encoded characters, because use_real_request_uri_unsafe changes that behavior and should be a deliberate decision. |
| allow and deny rules | ip-restriction plugin on routes or consumers | Confirm how client IP is determined behind load balancers. Address handling must match your real proxy chain or access rules will misfire. |
| auth_request subrequests | forward-auth plugin calling an external authorization service | Behavior of the auth service response and forwarded headers should be tested against the original auth_request logic. Expect the auth endpoint to become a dependency of every protected route. |
| Authentication at the edge (basic auth, JWT, API keys, OIDC) | basic-auth, jwt-auth, key-auth and openid-connect plugins | Consumers and credentials must be designed from scratch. OIDC flows such as authorization code and PKCE need client registration with your identity provider. |
| limit_req and limit_conn | limit-count plugin keyed on a chosen variable | Counters are local to each node by default, so a multi-node gateway enforces limits per node unless you configure redis, redis-cluster or redis-sentinel sharing. Set this before cutover. |
| Custom Lua modules, njs scripts and third-party NGINX modules | Custom plugins in Lua, plugin runners (Java, Go, Python) or WebAssembly | These need a plugin rewrite, not a copy. Inventory every module and script, then confirm the target language and test behavior against the original. |
| Reload-based changes (nginx -s reload) | Admin API calls or file-driven standalone mode with hot reload | Changes take effect without a process reload. Decide whether configuration is managed through the Admin API or the YAML or JSON file, and avoid mixing both for the same resources. |
Documented paths for importing and managing NGINX-derived routes
The Admin API is the runtime interface for creating, updating and deleting routes, upstreams, consumers and plugin configuration in a running APISIX instance. In traditional deployment mode, where the data plane and control plane run together, the Admin API listens on port 9180 while client traffic uses port 9080, according to the APISIX deployment modes documentation. Changes take effect without the reload cycle that NGINX configurations depend on, which removes one source of dropped connections during route changes.
- Port separation: Restrict network access to port 9180 to automation hosts and administrators, and keep it off client-facing networks.
- Scripted changes: Each NGINX
locationblock becomes an API call you can version, review and replay in a test environment. - Admin key handling: Replace the default Admin API key before any shared environment uses the API, and store it in your secrets system.
- Drift risk: Manual edits through the API can diverge from your source files, so pick one source of truth early.
- Rollback: Keep the previous route definitions in version control so you can reapply them if a change misbehaves.
Before relying on this path, confirm the Admin API version and endpoints for the APISIX release you will deploy in the project's current documentation. Check that your security team has approved the Admin API exposure model.
Standalone mode runs APISIX without etcd and loads all configuration from a YAML or JSON file, conventionally conf/apisix.yaml or conf/apisix.json. According to the deployment modes documentation, APISIX checks that file for changes every second and hot-reloads it without a restart. This makes the file itself the reviewable artifact, which fits teams that already manage NGINX as files in Git.
- Single source of truth: The file holds routes, upstreams and plugins, so a pull request shows exactly what will change at the gateway.
- No etcd dependency: Standalone mode removes the etcd cluster from the deployment, which reduces the components you must patch and back up, though it also means there is no distributed configuration store.
- Hot reload behavior: Confirm how a syntax error in the file is handled, because the one-second check can pick up a broken edit quickly.
- Multi-node consistency: Each node reads its own copy of the file, so your delivery pipeline must update every node to the same version.
- Trade-off on dynamic control: Standalone configuration is not the path for changes driven by other systems at runtime, so confirm your change process can live with file-based delivery.
Validate the file in a non-production instance with the same APISIX version before promoting it. Ask the project which features are limited in standalone mode for your target release.
ADC is the declarative configuration tool for APISIX, where you describe the desired gateway state in a file and the tool applies the difference to the running instance. Teams that want reviewable, repeatable changes without hand-written Admin API calls usually choose this path. Confirm the ADC release, supported resource types and sync behavior against the APISIX project documentation before you commit to it, because this area changes quickly.
- Desired-state model: You maintain one definition of routes, upstreams and plugins, and the tool computes what to create, change or delete.
- Review workflow: Route changes go through the same pull request and approval flow as application code.
- Environment overlays: Separate files or variables per environment keep test and production definitions aligned without copy-paste drift.
- Deletion behavior: Check how the tool handles resources removed from the file, since an accidental deletion can remove live routes.
- Secrets: Keep upstream credentials and plugin keys out of the definition file and inject them at apply time.
Ask whether the chosen tool supports the plugins your NGINX configuration relies on, such as proxy-rewrite and limit-count, and test each one before cutover.
This migration does not assume a fully automatic NGINX-to-APISIX converter. NGINX configurations often contain includes, variables, Lua blocks, conditional logic and custom headers that a tool may translate incorrectly or silently skip. Any third-party converter should be treated as a draft generator whose output is checked line by line against the original NGINX files.
- Source inventory first: List every server block, location, rewrite,
ifdirective, access rule and rate limit before running any tool. - Test with your own config: Run the converter on a copy of your real files and compare the generated routes with the original behavior.
- Human review: An engineer who understands the original intent signs off each translated route, especially auth and rewrite rules.
- Behavior tests: Send the same requests to NGINX and APISIX in a parallel environment and compare status codes, headers and upstream paths.
- Unsupported directives: Record anything the tool could not translate and assign an owner to design the APISIX equivalent by hand.
Before adopting a converter, ask its maintainers which NGINX directives they support and how they document unsupported ones.
How the NGINX to APISIX cutover runs in phases
The sequence moves routes from hand-written NGINX configuration to APISIX one group at a time, with each group proven under live traffic before the next one moves. Every phase ends at a documented exit criterion, and NGINX stays available as the rollback path until the last group is verified.
Inventory every NGINX configuration source
Collect nginx.conf, every included file, each server and location block, upstream definitions, map and geo blocks, rewrite and return directives, and any Lua or njs hooks. DeployOpen's engineers work through the inventory with your platform team, and your application owners confirm which paths and hostnames belong to which service. The step is done when every server block, location and hook has an owner and a planned disposition: translate, replace with a plugin, or retire.
Translate a pilot group of low-risk routes
Choose a small set of internal or low-traffic routes and translate them into APISIX routes and upstreams. Each location block becomes a route with matching rules, each proxy_pass target becomes an upstream, and each rewrite or header directive becomes the matching plugin configuration. The output is a version-controlled set of route definitions that you can review and load into a non-production APISIX instance.
Run APISIX beside NGINX with a traffic split
Keep NGINX serving production and place APISIX in front of, or beside, the pilot routes. Shift a small share of traffic to APISIX using a DNS weight, a load balancer weight or a split at an edge component, and keep that share under your control so it can be reduced to zero quickly. Your network team confirms that both paths reach the same upstream services and that health checks behave the same way on each side. The step closes when both paths carry traffic and dashboards show requests from each.
Validate authentication, headers, status codes and limits
Compare responses from the NGINX path and the APISIX path for the same requests. Check authentication outcomes for valid, expired and missing credentials, forwarded and removed headers, redirects, error pages and status codes, and rate-limit behavior, including the status returned when a limit is hit. Where APISIX rate limiting relies on local counters, confirm whether counts must be shared across nodes before relying on them. The step is done when every check in the agreed test set passes and any difference is either fixed or accepted in writing by the service owner.
Cut over group by group
Move the next route group only after the previous group has run cleanly under the split for the agreed observation period. Raise the APISIX share step by step, watching error rates, latency, upstream health and client-reported issues at each level. A group is cut over when it serves all production traffic from APISIX and its owner signs off on the results. Each cutover produces a short record of what moved, the validation results and any exceptions.
Keep NGINX configurations ready for rollback
Until each group has been stable on APISIX, retain its NGINX configuration, the matching DNS or load balancer weight, and a tested procedure to return traffic to NGINX. Store the NGINX files in version control with the APISIX definitions so both states can be restored from the same record. Rollback is rehearsed in a non-production environment before the first production cutover. The step closes when a rollback has been demonstrated for each group that has moved.
Decommission NGINX after final verification
When every route group has been stable on APISIX for the agreed period and no group still depends on NGINX, retire the NGINX traffic path. Remove unused NGINX hosts, certificates, Lua or njs modules and monitoring, and update runbooks, alerts and on-call documentation to point to APISIX. Keep an archived copy of the final NGINX configuration for audit and reference. The phase is complete when no production traffic reaches NGINX, the archive is stored, and the owners of each service have accepted the new operating model.
What drives the effort and cost of moving off NGINX
This table lists the factors that change how much discovery, translation, plugin work, testing and cutover labor a move from hand-written NGINX to APISIX will need. Read each row as a question to answer about your current configuration before asking for a quote. The main takeaway is that custom Lua or njs code, shared rate limit state and the test coverage you already have usually matter more than the raw count of server blocks.
| Driver | Why it changes effort or cost | How to keep it down or what to confirm |
|---|---|---|
| Server blocks and locations | Each server block and location becomes routes, upstreams and plugin settings. Many include rewrites and regex matches that need individual review. | Export the full configuration and group near-identical locations. Confirm how many distinct routing behaviors exist, not only the file count. |
| Custom Lua, njs or compiled modules | Lua in access_by_lua or njs scripts must be rewritten as APISIX Lua plugins, plugin runners or WebAssembly. Compiled third-party modules may have no direct equivalent. | Inventory every embedded script and module. Ask the partner which logic maps to built-in plugins and which needs a custom plugin. |
| Authentication wiring | auth_request subrequests, client certificate checks and JWT validation in NGINX each map to different APISIX plugins such as forward-auth, mtls and jwt-auth, with different consumer models. | List every auth path, the identity source behind it and which clients depend on the current headers. Confirm the target identity provider early. |
| Rate limit topology | Local counters are simple. Counters shared across nodes need a Redis, Redis Cluster or Redis Sentinel design, and limits spanning several routes need grouping decisions. | Document which limits are per node, per client or global, and whether they must hold across instances. Confirm the shared store you will operate. |
| Upstreams and health checks | Each upstream group, its load balancing method and its health check and failover behavior must be reproduced and then compared under load and failure. | Record upstream count, health check intervals and retry settings. Test failover on purpose during parallel running. |
| Deployment model | Traditional mode couples data and control planes, decoupled mode adds a shared etcd cluster to operate, and standalone mode drives configuration from a file or the Admin API. Each changes the build and the run work. | Choose the mode before estimating. Ask how configuration changes will be reviewed, stored and rolled out in your change process. |
| Test coverage for parallel comparison | Without automated tests or recorded traffic, side-by-side comparison depends on manual checks, which adds labor to every cutover step. | Check whether you have integration tests, traffic captures or logs that can replay real requests. Ask what the partner will build for comparison. |
| Recurring operations | Operating etcd, backups and certificate rotation recur after cutover. APISIX upgrades also need regression testing of plugins and custom code each cycle. | Ask who owns etcd, how upgrades are tested and whether a retainer covers upgrades. Ask for these items as separate line items in the quote. |
Questions teams ask before moving from NGINX to APISIX
How much effort does moving from hand-written NGINX to APISIX take?
Effort depends mainly on how many server and location blocks you run, how much logic lives in custom Lua, njs or rewrite rules, and how many auth and rate limit policies must be ported. Each location block usually becomes a route with an upstream, and each directive with side effects becomes a plugin configuration to test. Ask any partner for a scope that lists every server block, location, upstream, custom module and reload hook, then price each group separately. Drivers in the quote should be the count of routes, the number of distinct policies, and the amount of custom code that has no direct plugin equivalent.
How long does a parallel run usually need?
The length of a parallel run depends on traffic patterns, how many consumers call each API, and how much confidence your team needs before retiring the old proxy. Run both gateways behind the same hostnames or a split at the load balancer, compare status codes, latency and upstream selection, and move low-risk routes first. Your traffic data sets the window: include at least one full business cycle, including month-end or batch jobs if they touch the gateway. Confirm the exact cutover mechanics with the vendor, since they depend on your load balancer and DNS setup.
Can our own team do the NGINX to APISIX migration in-house?
A team that already operates NGINX and has someone comfortable with YAML, the Admin API and etcd can do the work in-house, especially for a small number of routes. The skills that take longest to build are translating rewrite and conditional logic, designing consumer and auth models, and setting up tests that prove behavior is unchanged. APISIX documents its deployment modes, including standalone mode, which can be useful for teams that prefer file-driven configuration under version control. A partner is most useful when the scope is large, the timeline is fixed, or security review requires independent evidence.
What must we provide before a migration starts?
Provide the current NGINX configuration files in a version-controlled repository, including includes, maps, Lua or njs modules and TLS settings. Traffic data from access logs is needed to rank routes by volume and to build parity tests. For each route, name an owner who can confirm expected behavior, approve the cutover and sign off on rollback. Also supply the identity provider details, existing rate limit rules, and a list of upstreams with health and certificate requirements. Missing owners are the most common reason migrations stall, so confirm them before scheduling work.
Do our existing authentication and rate limits carry over to APISIX?
Most of them can be ported, but each must be mapped to a specific APISIX plugin and tested. Basic and key-based checks map to plugins such as key-auth or basic-auth, JWT validation maps to jwt-auth, and OpenID Connect flows use the openid-connect plugin, which supports identity providers such as Keycloak, Microsoft Entra ID and Okta. Rate limits map to limit-count, which keeps counters in local memory by default or shares them through Redis, Redis Cluster or Redis Sentinel. Confirm with the vendor how a counter shared across routes or nodes behaves under your expected load, and whether sliding windows match your current policy.
How does rollback work if the new gateway misbehaves?
Rollback should be designed before the first route moves. The usual approach keeps the NGINX configuration running and deployable, and shifts traffic back at the load balancer or DNS layer once the old path is restored. Because APISIX changes take effect through the Admin API or configuration files rather than a full reload of the process, you should still record the last known good configuration for each route. Test the rollback in a non-production environment, time how long it takes your team to execute, and agree the trigger conditions in writing before cutover.
Where NGINX to APISIX migrations usually go wrong
Moving hand-written NGINX configuration into APISIX changes how state, requests and changes are handled, not just the syntax. The risks below tend to surface after cutover, when traffic behaves differently from the old proxy. Each card names the check to run before traffic moves.
Rate limits stay local per node
The limit-count plugin keeps counters in local memory by default, so each APISIX node enforces its own count. A limit that worked as one shared counter in a single NGINX instance can quietly multiply across a cluster. Decide whether the counter must be shared, then configure a Redis-backed policy and give related routes the same group so they share one counter. Test the limit with requests spread across every node.
URI normalization changes upstream paths
The proxy-rewrite plugin normalizes the URI by default before forwarding, which can alter paths that NGINX passed through untouched, such as encoded characters or dot segments. Some backends depend on the exact raw path. Compare upstream request paths captured from the old and new gateways for a representative sample. Only set use_real_request_uri_unsafe after reviewing the security implications with your team.
Custom modules found too late
Hand-written NGINX setups often include Lua scripts, njs code, custom headers or compiled modules that nobody lists in the architecture. These behaviors disappear silently at cutover. Inventory every include, directive and module in the nginx.conf tree and each server's build, then map each one to a built-in plugin, a custom plugin or a removal decision. Record the decision before the first parallel run.
Authentication differs from auth_request
An NGINX auth_request subrequest often treats the upstream auth response as a simple allow or deny and forwards selected headers. APISIX authentication plugins such as openid-connect, jwt-auth and forward-auth have their own rules for token validation, header propagation, error codes and consumer identity. Test both allowed and denied requests, expired tokens and missing headers against each route, and confirm which identity headers the backend receives.
Admin API exposed during the move
During migration, teams often run the Admin API on a reachable address to push routes quickly. In traditional mode the Admin API listens on port 9180, which should never face client networks or the public internet. Bind it to a management interface, restrict access by network policy and admin keys, and rotate default credentials before any environment is shared. Include the Admin API in the same review as the client listener.
Reload-based change review disappears
An NGINX change usually passes through a config file, a diff, a test with nginx -t and a reload. APISIX changes can reach a running gateway through the Admin API or a file watched in standalone mode, so a bad change can take effect without the usual gate. Store route definitions in version control, require peer review, and validate them in a staging gateway before promotion. Agree who may change routes in production and log every change.
What changes for your operations team after cutover
The biggest change is where configuration changes are made and approved. On NGINX, an engineer edits nginx.conf or an included file, runs a syntax test, and reloads the process. On APISIX, routes, upstreams and plugin settings live in etcd in traditional and decoupled modes, and they are changed through the Admin API on port 9180, or through a declarative file in standalone mode. Your team should decide, before cutover, which of these two paths is the source of truth. For most teams, that means keeping route and plugin definitions in a Git repository, reviewing them as pull requests, and applying them through a pipeline. The Admin API then becomes the deployment target, not a place where engineers make ad hoc edits. Confirm with your own environment whether your change process requires a record of who changed which route, and whether the Admin API audit trail meets it. Standalone mode, where APISIX watches conf/apisix.yaml or conf/apisix.json and hot-reloads each second, changes the rollback path, so test that path before relying on it.
Monitoring also changes. NGINX access logs and a few stub status counters often sufficed for a reverse proxy. APISIX needs its own access and error logging, metrics and tracing plan, and the plugin set you chose determines what it can emit. Decide which fields your security and incident teams need from each request, such as route identifier, consumer, upstream response time and rejected-request counts from limit-count, then configure the logging and observability plugins to produce them and send them to the same destination your SIEM already reads. Add alerts for etcd health and for Admin API errors, because a failing control plane can block changes even when traffic is flowing normally.
Recurring work also shifts. Someone must own APISIX version upgrades, which means reading each release's upgrade notes, testing plugin compatibility in a non-production environment and scheduling the change window. Your team must also care for etcd: monitoring its health, managing backups and certificates, and planning how to restore it if the cluster loses quorum. Ask your implementation partner, or your own platform team, to document these tasks with owners and frequency before the cutover date, and check the release and support status on the Apache APISIX downloads page for the version you deploy.

