Why the Docker Compose evaluation setup is not a production APISIX deployment
A team that has run the Docker Compose quickstart has proven that APISIX starts, routes a request and serves its Admin API. That is useful, but the APISIX installation guide lists Docker Compose as the option for evaluation, not as a production runbook. Moving to production means making decisions the quickstart makes for you: whether configuration lives in an external etcd cluster or in file-driven standalone mode, how the admin key is supplied to the gateway process, which ports are reachable from which networks, how configuration changes are applied, and how image upgrades are rolled out and rolled back. Each of these choices affects security and recovery, so none of them should be inherited by accident from a demo file.
The first decision is the configuration store. In traditional and decoupled modes, APISIX depends on an etcd cluster, and the installation guide asks for a supported etcd release in those modes. In standalone mode there is no etcd, and configuration comes from conf/apisix.yaml or conf/apisix.json. Ask your team which mode you will run, who owns the etcd cluster if you choose it, and how configuration files will be versioned and delivered into containers. Confirm the supported etcd release for your APISIX version against the installation guide before you pin images.
The second decision is the admin key. The installation guide says that an ADMIN_KEY variable referenced in config.yaml must exist in the APISIX process environment, not only in an interactive shell. A container that works when you exec into it may still fail at startup if the orchestrator does not inject the variable. Plan where the value is stored, how it is rotated, and how you verify that the running process can read it. Also confirm which ports you publish: the guide lists 9180 for the Admin API as a port to restrict to operator networks and 9092 for the Control API as one never to expose publicly, so the Compose port mappings used in evaluation should not carry into production unchanged.
Choosing etcd-backed or standalone configuration for Docker
In traditional mode, the data plane and control plane run together in the same APISIX container, and both depend on an etcd cluster for configuration. Client traffic arrives on port 9080, and the Admin API listens on port 9180 for changes. This is the simplest etcd-backed layout for a small set of hosts, because each container is both the gateway and the administrative endpoint. The team still needs to operate etcd as a stateful service with its own storage, backups and upgrades.
- What you run: APISIX containers plus a supported etcd release, with the Admin API reachable only from operator networks.
- What you back up: the etcd data directory, since route, consumer and plugin configuration lives there.
- Who manages state: changes go through the Admin API, so every route change needs an auditable process around it.
- Failure impact: APISIX depends on etcd for configuration changes, so plan for what happens when etcd is unavailable.
- Platform fit: APISIX supports Linux for production, so confirm your container host operating system is on the supported list.
Confirm with your platform team who will own the etcd cluster and whether that ownership is already staffed.
Decoupled mode separates the data plane from the control plane. Gateway nodes that handle traffic are kept apart from the nodes that accept administrative changes, and both sets share one etcd cluster. This suits organizations that want to keep the Admin API on a small, tightly controlled set of hosts while running many gateway containers across several networks. The trade-off is a more complex topology that needs careful network design between tiers and a stronger etcd operating model.
- What you run: separate control plane and data plane container groups, both pointing at one shared etcd cluster.
- Network design: the data plane needs access to etcd, while the Admin API stays on operator networks.
- Scaling: gateway replicas can grow without exposing administrative endpoints on each one.
- Shared dependency: one etcd cluster serves both tiers, so its sizing, placement and access controls affect the whole estate.
- Team skills: expect more moving parts to document, monitor and rehearse during incidents.
Ask your network and security teams to confirm which hosts can reach etcd and the Admin API before any container is started.
Standalone mode runs without etcd. APISIX reads its configuration from a YAML or JSON file, conf/apisix.yaml or conf/apisix.json, and checks that file for changes every second, reloading without a restart. For container hosts this means the configuration becomes an artifact: you build it, version it, review it and mount it into each container. The team takes on less stateful infrastructure, but it must own the delivery pipeline that keeps every gateway container on the same file.
- What you run: APISIX containers with a mounted configuration file and no etcd cluster to operate.
- What you version: the configuration file itself, stored in source control with review before release.
- Delivery: a pipeline or orchestrator must place the same file on every container and confirm each one has loaded it.
- Environment variables: in standalone mode, Docker environment variables added after initialization take effect only after a reload, so plan that step explicitly.
- Feature fit: some features depend on values that etcd-backed setups normally generate, so confirm the features you need work in this mode.
Verify with your vendor documentation which features and admin-side behaviors differ in standalone mode before you commit to it.
The right mode depends less on traffic volume than on who will operate each dependency. An etcd-backed mode moves the work toward a stateful cluster that must be run, backed up and upgraded. Standalone mode moves the work toward release engineering, where the configuration file and its delivery path become the source of truth. Both approaches are supported production patterns, so the decision should follow the skills and controls your organization already has.
- Operating skills: etcd-backed modes need people who can run a distributed key-value store safely.
- Change control: standalone suits teams already running configuration through Git review and automated release.
- Audit trail: confirm where each configuration change is recorded in each mode.
- Recovery: compare how each mode restores service after a host or cluster failure.
- Upgrades: etcd-backed modes add an etcd upgrade track to the APISIX upgrade track.
Record the decision in writing, including who owns the etcd cluster or the configuration pipeline, before you write the Compose or container definitions.
Port and configuration options and when each fits
This table compares each port and configuration input that a production APISIX container exposes or reads, with the deployment pattern it suits and what to confirm in your network. Use it as a checklist before publishing any container to a network segment. The main takeaway is that only client traffic should face users, while the Admin API, Control API, metrics and etcd should stay on operator or internal networks.
| Port or setting | When it fits | What to confirm in the target network |
|---|---|---|
| 9080 (client traffic) | Publish to consumers or a load balancer that fronts the gateway. This is the only port that should face API clients. | Which load balancer or ingress reaches the container, that TLS terminates where your policy requires, and that upstream services accept traffic only from the gateway. |
| 9180 (Admin API) | Use for configuration changes by operators or automation in traditional or decoupled modes. Restrict access to operator networks. | Which subnets or bastion hosts may reach it, that the admin key is not the default, and that no public route or port mapping exposes it. |
| 9092 (Control API) | Use only for internal health and status checks from the host, orchestrator or a private monitoring path. Keep it private at all times. | That no port mapping publishes it, that probes run from inside the private network, and that firewall rules block it from every external range. |
| 9091 (metrics) | Expose to your monitoring system, such as a Prometheus scraper on an internal network, to collect gateway metrics. | Which scraper addresses are allowed, whether metrics contain route or consumer detail you must protect, and how the scrape path is authenticated. |
| 2379 (etcd) | Relevant only in etcd-backed traditional or decoupled deployments, where the gateway containers must reach a shared etcd cluster. | That the etcd endpoint is reachable only from gateway and control plane hosts, that client certificates or mTLS are configured as your policy requires, and the supported etcd release for your APISIX version. |
| ADMIN_KEY (environment variable) | Supplies the admin key that config.yaml references. Pass it to the APISIX process environment through your container runtime or secret store, not only through an interactive shell. | That the value comes from your secret manager, that it is set at container start, and that it is rotated through a documented procedure rather than baked into an image or Compose file. |
| Standalone configuration file (apisix.yaml) | Use when you run without etcd and deliver routes as a file mounted into the container, with changes applied by hot reload. | That the file is mounted from a version-controlled source, that the reload check interval and file ownership suit your change process, and that Docker environment variables added after startup take effect only after a reload. |
How the admin key reaches the APISIX process in containers
The ADMIN_KEY value referenced in config.yaml must exist in the APISIX process environment, not only in an interactive shell. The container runtime has to pass the key when the container starts, because a value exported in an operator's terminal never reaches the process that reads the configuration. Before go-live, confirm that the variable is present inside the running container (for example by checking the process environment of the APISIX process rather than an exec session), and that the same variable name is used in every environment so the configuration file does not change between staging and production.
Choose an injection path that keeps the key out of the image and out of version control. Do not bake the key into an image layer, where anyone who can pull the image can read it, and do not commit a Compose file or deployment manifest that contains the literal value. Instead, pull the value at start from the secret store your organization already operates, using the injection mechanism your container platform provides, and keep the manifest that references the secret in Git. Confirm with your platform team who can read the secret, how access is logged, and whether the Admin API is reachable only from operator networks, since possession of the key is what grants administrative access to routes and plugins.
Plan rotation before the first deployment. In standalone mode, Docker environment variables added after initialization take effect only after a reload, so changing the key in the secret store does not update a running gateway by itself. Write the rotation runbook as a sequence: update the secret, restart or reload the containers in a controlled order, verify the Admin API accepts the new key and rejects the old one, and then revoke the old value. Ask DeployOpen or your own team to test this sequence in a non-production environment, because a missed container during rotation leaves operators locked out of the Admin API or running with a key that no longer matches the configuration.
Steps to move APISIX from Compose to production containers
This sequence turns a Docker Compose evaluation into a production container deployment with a chosen configuration mode, a secret supplied through the process environment, and only the ports clients need exposed. Each step ends with a check you can run before moving on.
Choose the configuration mode
The platform team decides between traditional or decoupled mode with an external etcd cluster and file-driven standalone mode without etcd. The choice depends on whether you need dynamic Admin API changes across replicas or prefer configuration held in a version-controlled file. Record the decision and the APISIX image tag in the change request. The step is complete when the team can state the mode, the owner of the configuration, and how changes reach running containers.
Provision etcd or prepare the standalone file
In etcd-backed modes, the infrastructure team provisions an etcd cluster on a supported release that the APISIX installation guide lists, and places it on a network reachable only from gateway and operator hosts. In standalone mode, the team writes conf/apisix.yaml or conf/apisix.json and mounts it into the container. Check the setup by running a health query against etcd from a gateway host, or by validating the file syntax in a non-production container before the first start.
Inject the admin key into the container environment
The operator stores the value referenced by ADMIN_KEY in config.yaml in a secret store and passes it to the container through the runtime's environment settings, not an interactive shell export. The APISIX installation guide requires the variable to exist in the APISIX process environment. Confirm it by running a command inside the running container that reads the process environment, then start the Admin API and check that a request without the key is rejected. In standalone mode, remember that variables added after initialization take effect only after a reload.
Publish only the client port
The operator maps host port 9080 to the container for client traffic. Map 9443 only if TLS is terminated at APISIX, and confirm the TLS port with the installation guide and your certificate setup before publishing it. Do not publish 9092, the Control API, or 9091, metrics, to any network segment that clients can reach. Verify the mapping by listing published ports on the host and running a scan from outside the trusted network that shows only the intended client ports open.
Restrict the Admin API to operator networks
Port 9180 serves the Admin API and should be reachable only from operator networks or a bastion host. Apply a host firewall rule, a security group, or a network policy that limits access to those sources, and keep 2379 limited to gateway and etcd members. The security team confirms the rule by testing a connection to 9180 from a client network, which should fail, and from an operator network, which should succeed and return an authentication error without the key.
Keep the Control API private
Port 9092 serves the Control API and must never be exposed publicly. Bind it to the loopback or an internal interface if your configuration allows it, and block it at the host and network layers. The operator confirms the state by checking that the container's listening sockets show 9092 on an internal address only and that a connection attempt from outside the cluster or host network fails.
Verify with a route test
The operator creates a test route that points to a non-production upstream, using the Admin API in traditional mode or the standalone file in file mode. Send a request to port 9080 from a client network and confirm the expected upstream response. In file mode, change the file, wait for the one-second check cycle, and confirm the new route answers without a restart. Remove the test route after the check so it does not reach production traffic.
Record the image version and upgrade path
The operator records the exact image tag and digest, the etcd version, the configuration mode, and the mounted file or secret references in the runbook. Review the release notes for the chosen version before each image upgrade, and test the new image in a staging container against the same configuration. The step is complete when the runbook names the owner of each record and an approver for future image changes.
What reloads, what restarts and when a config change takes effect
The first thing to settle before a release is which kind of change you are making, because APISIX and Docker handle the two differently. In file-driven standalone mode, APISIX checks conf/apisix.yaml or conf/apisix.json for changes every second and hot-reloads them without a restart, so a route or plugin edit to the mounted file takes effect within seconds. Environment variables are a different case. A value added to the container after APISIX has initialized, such as a new ADMIN_KEY reference in config.yaml, is not picked up until APISIX reloads, and a plain container restart is the usual way to force that. Treat the file for routing changes and the process environment for secrets as two separate release paths.
This split shapes rolling container updates and secret changes. If you replace containers one at a time, each new container reads its environment and its mounted file at startup, so the old and new replicas can serve different configuration for a period. Confirm with the vendor and in your own environment how your chosen image and mount handle a file that changes during a rolling replacement, and decide whether a mixed-version window is acceptable for your traffic. For admin key rotation, plan the sequence explicitly: the new value must reach the APISIX process environment, the process must reload or restart, and any operator tooling must switch keys at the same time. Rotating the key in an interactive shell on the host does not change what the running container sees.
For config promotion, the test that matters is a dry run against an image and file set that matches production, not the Compose evaluation setup. Before a release, run the candidate image with the production file mounted and the production environment supplied, edit a route in the mounted file and confirm the change appears within the expected one-second check window, then change an environment value and confirm it only takes effect after the reload you intend. Also test the failure path: introduce a malformed file in staging and check that the gateway keeps serving its last good configuration and that your monitoring raises an alert. Record the results as part of the release evidence.
Security controls for APISIX containers in production
A production APISIX container exposes several ports and holds an admin credential that can change every route and plugin. The controls below narrow who can reach those ports, keep secrets out of images and repositories, limit what a compromised container can do, and leave a record of every change. Work through them together before the first production deployment.
Restrict the Admin API port
Port 9180 serves the Admin API, which can create, change and delete routes, consumers and plugins. The installation guide asks that it be restricted to operator networks. In Docker, do not publish 9180 to the public interface. Publish it only on an internal network or bind it to a management address, and confirm that no load balancer or ingress rule forwards internet traffic to it. Test the restriction from outside the operator network.
Never expose the Control API
Port 9092 is the Control API, which the installation guide says must never be exposed publicly. Do not map it in your Compose file or container run command, and do not include it in a public load balancer target group. Health checks for orchestrators should reach it only from inside the host or cluster network. Confirm the port is closed on the host firewall as well as in container configuration.
Keep the admin key out of images
The ADMIN_KEY value referenced in config.yaml must exist in the APISIX process environment. Pass it at runtime through your secret store or an orchestrator secret mechanism, not through a Dockerfile ENV line, a build argument or a committed .env file. Image layers persist in registries, so a key baked into an image stays readable to anyone with pull access. Rotate the key if it has ever been committed or stored in a shared image.
Limit container capabilities and user
Reduce what the APISIX container can do if it is compromised. Drop Linux capabilities that the gateway does not need, set the root filesystem to read-only where the configuration allows it, and block privilege escalation. Check whether the published image runs as a non-root user and whether your chosen tag supports that. Confirm the exact user, writable paths and required capabilities with the image version you deploy, then test the container with those settings before rollout.
Protect access to etcd
In etcd-backed modes, the etcd port 2379 holds the complete route, consumer and plugin configuration, so anyone who reaches it can change traffic behavior without touching APISIX. Place etcd on a private network reachable only by APISIX and etcd peers, and enable client authentication and transport encryption. Confirm the supported etcd release for your APISIX version, and check that no container publishes 2379 to the host or wider network.
Log every admin change
Route, consumer and plugin changes made through the Admin API alter live traffic, so each change needs an accountable record. Forward APISIX access and error logs to your central log platform, and record who changed configuration through your deployment pipeline or API client. Keep Admin API requests traceable to a named operator or automation identity. Confirm which log fields your APISIX version emits and how long your organization must retain them.
Questions teams ask before running APISIX in Docker
What drives the cost of running APISIX in Docker for production?
The cost depends mainly on how many environments you run, where etcd lives, and how much of the surrounding process you must build yourself. The main components are the design work (deployment mode, etcd or standalone, port exposure), the build of container images and runtime configuration, the secrets process for the admin key, testing of config reloads, and handover documentation. Most of this is one-time work. Recurring costs are the upgrade cycles, the etcd and backup operations if you keep etcd, and any retainer you choose for ongoing support. Ask any quote to separate these lines and to state which environments, which networks and which deployment mode it assumes.
How long does a production Docker setup for APISIX usually take?
The duration is set by scope rather than by the APISIX installation itself, which is quick in an evaluation. The timing depends on the number of environments, whether you need an external etcd cluster or file-driven standalone configuration, how your secrets platform delivers the admin key, the network rules you must get approved, and how many routes and plugins must be verified after the move. Dependencies on other teams, such as security review of exposed ports or a change window, often take longer than the build. DeployOpen agrees timelines in writing as part of a fixed-scope plan, so ask for milestones tied to each of these items.
What does the work need from our team before it starts?
Four things need to be decided or provided before build work begins. First, network access: which hosts can reach the Admin API on port 9180, which can reach metrics on 9091, and which are client-facing on 9080. Port 9092 for the Control API should never be exposed publicly. Second, etcd ownership: you must decide whether your team runs the etcd cluster on port 2379 or whether the gateway uses standalone mode with no etcd. Third, a secrets process that injects the ADMIN_KEY variable into the APISIX process environment, not only an interactive shell. Fourth, named owners for approvals, change windows and access to your container registry.
Can an internal platform team run APISIX in Docker without outside help?
Yes, a team with container and Linux experience can run APISIX in Docker without outside help, and the official installation guide covers the supported paths. The harder parts are design decisions that affect later operations: choosing between traditional, decoupled and standalone modes, deciding how reloads are tested, designing the admin key handling, and keeping the Admin and Control APIs off public networks. Teams without prior gateway operations often find the upgrade and rollback procedure the most demanding part. A partner can plan and build the setup, or review your existing design, so the internal team carries the knowledge after handover.
Why does the ADMIN_KEY variable sometimes fail after deployment?
The most common cause is that ADMIN_KEY is referenced in config.yaml but is missing from the APISIX process environment. The variable must exist in the environment of the running process, so setting it only in an interactive shell or a build step is not enough. In file-driven standalone mode there is a second trap: Docker environment variables added after initialization take effect only after a reload. Check the container runtime definition, confirm the variable inside the running container, and include a reload test in the change plan. Your secrets tooling should inject the value at start time, and rotation should be part of the runbook.
How are APISIX image upgrades scheduled in a production container setup?
Upgrades should follow a planned cycle, not be applied when a new image appears. Before scheduling, check the release notes and upgrade guide for the target version, because configuration keys and plugin behavior can change between releases. Current releases referenced on the downloads page include APISIX 3.19.0, and that page does not mark any release as long-term support, so confirm your support window with the project before choosing a target. Test the new image in a staging environment with a copy of your configuration, verify routes and plugins, then roll out with a documented rollback image tag. Ask any quote to include the upgrade procedure and its test evidence.
How DeployOpen works on this
DeployOpen plans, builds and hands over self-hosted APISIX container deployments inside your own infrastructure, starting from a written scope. The work moves a Docker Compose evaluation setup into production containers, with the configuration mode, secrets handling, network exposure and upgrade path agreed before any build begins.
How we scope
- Written scope records the APISIX version, deployment mode, etcd or standalone choice and target container runtime.
- Port exposure is mapped per listener, including client traffic, the Admin API and the Control API, with the operator networks named.
- Engagements are fixed-scope, so deliverables and timelines are agreed in writing before work starts.
- Scope lists which upstream services, identity providers and plugins the gateway must reach from inside your network.
How we build
- Container images, compose or orchestration files and environment configuration are built for your registry and hosts.
- The admin key is injected through the process environment and verified after container start, not only in an interactive shell.
- Configuration reload behavior is tested by changing routes and environment values, then confirming which changes need a reload.
- Routes, authentication and plugins are checked against the agreed test cases before cutover.
How we hand over
- Documentation covers the runbook for starting, stopping, reloading and upgrading the container set in your environment.
- Image upgrade steps, rollback points and version pinning rules are written for your operations team to follow.
- Network and port exposure rules are documented so firewall owners can confirm that the Admin and Control APIs stay restricted.
- Knowledge transfer sessions walk your engineers through the deployed setup and the open items agreed in scope.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers after handover.
- Upgrade work follows the APISIX release notes and project upgrade guidance, with a test pass before each production change.
- Your team keeps ownership of the environment, and DeployOpen can support changes to the container set on request.
- Security testing of the gateway can be scoped alongside the deployment, with findings tracked against the agreed plan.

