Skip to content
Get Started for Free

Deploy LocalStack Operator

The LocalStack Operator is our Kubernetes-native way to deploy and manage LocalStack instances. It abstracts Kubernetes-specific configuration and automates operational tasks, making LocalStack deployments more consistent and easier to maintain. It can manage multiple LocalStack instances within a cluster to provide isolated local clouds for multiple users.

The Operator manages the full lifecycle of LocalStack resources and enables advanced Kubernetes integrations that are difficult to configure manually.

This guide explains how to deploy and manage LocalStack in a Kubernetes cluster using the LocalStack Operator.

The Operator supports the following advanced capabilities:

  • Opt-in DNS and endpoint injection for Kubernetes workloads
  • Cluster DNS configuration to resolve AWS-style subdomains in the same namespace
  • Automatic loading of Cloud Pods on startup
  • Support for initialization hooks
  • Simplified logging configuration
  • Automatic mounting of a PersistentVolumeClaim (PVC) for the LocalStack data directory, enabling artifact caching and persistence

Before installing the LocalStack Operator, ensure you have:

  • A running Kubernetes cluster
  • A LocalStack license that includes Kubernetes features
  • An authentication token for that license

The easiest way to install the Operator controller is to apply the published manifests directly from GitHub.

Terminal window
# Install the latest version
kubectl apply -f https://github.com/localstack/localstack-operator/releases/latest/download/controller.yaml

To install a specific version:

Terminal window
# Example: install v0.4.0
kubectl apply -f https://github.com/localstack/localstack-operator/releases/v0.4.0/download/controller.yaml

See the Operator releases page for all available versions.

Once the Operator is running, you can deploy a LocalStack instance by creating a LocalStack custom resource.

A minimal example looks like this:

apiVersion: api.localstack.cloud/v1alpha1
kind: LocalStack
metadata:
name: localstack
namespace: my-namespace
spec:
image: localstack/localstack-pro:latest
dnsProvider: coredns
dnsConfigName: coredns
dnsConfigNamespace: kube-system
envFrom:
- secretRef:
name: localstack-auth-token

In this example, the LocalStack auth token is read from a Kubernetes Secret named localstack-auth-token.

You can create this Secret with:

Terminal window
kubectl create secret generic localstack-auth-token \
--from-literal=LOCALSTACK_AUTH_TOKEN="$LOCALSTACK_AUTH_TOKEN"

With this example, the auth token must be available in the LOCALSTACK_AUTH_TOKEN environment variable when creating the Secret.

By default, the Operator creates a ClusterIP Service named:

localstack-<crd-name>

For the example above (name: localstack), the Service name is:

localstack-localstack

This Service exposes:

  • The LocalStack gateway port (4566)
  • AWS service ports
  • Port 53 for DNS

Using standard Kubernetes DNS resolution, the Service can be reached at:

  • localstack-localstack (same namespace)
  • localstack-localstack.my-namespace
  • localstack-localstack.my-namespace.svc.cluster.local

When dnsProvider: coredns is configured, LocalStack can also be reached through any subdomain of these service names.

Inject LocalStack endpoints into workloads

Section titled “Inject LocalStack endpoints into workloads”

The Operator can configure individual workloads to use the LocalStack instance in their namespace without changing cluster-wide DNS. It uses an admission webhook to update opted-in Pods when Kubernetes creates them. Pods without the opt-in label are not affected.

Add the localstack.cloud/inject-dns: "true" label to the Pod template of each Deployment, StatefulSet, Job, or other workload that needs to access LocalStack:

spec:
template:
metadata:
labels:
localstack.cloud/inject-dns: "true"

Add the same label directly under metadata.labels when you create a standalone Pod.

For an opted-in Pod, the Operator:

  • Configures the Pod’s DNS to send requests to LocalStack first, before falling back to the cluster DNS resolver. LocalStack only responds to domain names it owns, that is, those ending in localhost.localstack.cloud or your configured $LOCALSTACK_HOST, so requests such as my-bucket.s3.localhost.localstack.cloud resolve to LocalStack while every other DNS request, including in-cluster Service names, is forwarded to the cluster resolver as normal.
  • Sets AWS_ENDPOINT_URL to http://localstack-<localstack-name>.<namespace>:4566 in every regular, init, and ephemeral container. AWS SDKs and tools that support this setting use the LocalStack endpoint without application-specific endpoint configuration.

If a container already defines AWS_ENDPOINT_URL, the Operator replaces its value with the LocalStack endpoint. An endpoint configured directly in application code, with the AWS CLI --endpoint-url option, or with a service-specific endpoint environment variable takes precedence over AWS_ENDPOINT_URL.

The namespace must contain exactly one LocalStack resource. If the namespace contains no LocalStack resources or more than one, the Operator creates the Pod without injecting DNS or the endpoint. The Operator also skips injection when the LocalStack Service does not have a usable ClusterIP.

Injection occurs only when Kubernetes creates a Pod. After adding the label to an existing workload, recreate its Pods to apply the configuration.

The webhook does not block Pod creation. If the Operator or LocalStack Service is unavailable, Kubernetes creates the Pod without the injected configuration.

The endpoint injection webhook uses TLS. Choose how the Operator manages its serving certificate when you install the Operator.

The default self-managed mode requires no additional cluster components. The Operator creates and rotates the certificate and keeps the Kubernetes API server’s trust configuration up to date. Existing installations continue to use this mode without configuration changes.

Use cert-manager mode to delegate certificate issuance and rotation to cert-manager. Install cert-manager and create an Issuer or ClusterIssuer before starting the Operator in this mode. The Operator reports an error at startup if the cert-manager Certificate resource is unavailable.

Configure the Operator manager with the following flags or equivalent environment variables:

Flag Environment variable Default Description
--certificate-mode CERTIFICATE_MODE self-managed Set to cert-manager to delegate certificate management.
--certificate-issuer-name CERTIFICATE_ISSUER_NAME Unset Name of the issuer. Required in cert-manager mode.
--certificate-issuer-kind CERTIFICATE_ISSUER_KIND ClusterIssuer Set to Issuer or ClusterIssuer.

When you install the Operator from the published controller.yaml, add the environment variables to the Operator’s ConfigMap and restart the manager:

Terminal window
kubectl patch configmap localstack-operator-controller-manager-config \
--namespace localstack-operator-system \
--type merge \
--patch '{"data":{"CERTIFICATE_MODE":"cert-manager","CERTIFICATE_ISSUER_NAME":"<issuer-name>","CERTIFICATE_ISSUER_KIND":"ClusterIssuer"}}'
kubectl rollout restart deployment/localstack-operator-controller-manager \
--namespace localstack-operator-system

Replace <issuer-name> with the name of your issuer. Set CERTIFICATE_ISSUER_KIND to Issuer if you use a namespaced issuer.

Use a cert-manager CA issuer rather than a SelfSigned issuer.

The webhook does not block Pod creation while you switch certificate modes.

The LocalStack Operator introduces a LocalStack Custom Resource Definition (CRD) that controls how LocalStack instances are deployed and configured.

CRD documentation is currently maintained manually. For a full reference of available fields, see: https://github.com/localstack/localstack-operator/blob/main/api-docs.md

The Operator manifest creates all required Roles, ClusterRoles, and bindings.

KindNameAPI GroupsResourcesVerbs
Rolelocalstack-operator-leader-election-roleconfigmapsget, list, watch, create, update, patch, delete
coordination.k8s.ioleasesget, list, watch, create, update, patch, delete
eventscreate, patch
ClusterRolelocalstack-operator-manager-roleconfigmapsdelete, get, list, patch, update, watch
eventscreate, patch
pods, pods/exec, pods/logcreate, delete, deletecollection, get, list, patch, update, watch
secretsget, list, watch
serviceaccountscreate, delete, get, list, update, watch
servicescreate, delete, get, list, patch, update, watch
api.localstack.cloudlocalstackscreate, delete, get, list, patch, update, watch
api.localstack.cloudlocalstacks/finalizersupdate
api.localstack.cloudlocalstacks/statusget, patch, update
appsdeploymentscreate, delete, get, list, patch, update, watch
appsdeployments/scalecreate, delete, get, list, patch, update, watch
rbac.authorization.k8s.iorolebindings, rolescreate, delete, list, update, watch
ClusterRolelocalstack-operator-metrics-reader (nonResourceURLs: /metrics)get
ClusterRolelocalstack-operator-proxy-roleauthentication.k8s.iotokenreviewscreate
authorization.k8s.iosubjectaccessreviewscreate

This ClusterRole allows the Operator to manage LocalStack resources and related Kubernetes objects.

Resources include:

  • Pods (including exec and logs)
  • Services
  • Secrets
  • Deployments
  • ServiceAccounts
  • LocalStack CRDs (localstacks, status, finalizers)
  • RBAC roles and role bindings

Verbs include:

create, delete, get, list, watch, patch, update

Additional ClusterRoles are created for:

  • Reading metrics (/metrics)
  • Authentication and authorization reviews (tokenreviews, subjectaccessreviews)

With dnsProvider: coredns, the LocalStack Operator configures cluster DNS to forward AWS-style subdomain requests to the LocalStack DNS server.

This enables features such as:

  • S3 virtual-host–style addressing
  • API Gateway domain name resolution

Example from another pod in the cluster:

Terminal window
aws apigatewayv2 create-api \
--name testGatewayProxy \
--protocol-type HTTP \
--target "https://httpbin.org"

Example response:

{
"ApiEndpoint": "http://1d4b6907.execute-api.localstack-localstack.my-namespace:4566",
"ApiId": "1d4b6907"
}

Calling the API:

Terminal window
curl http://1d4b6907.execute-api.localstack-localstack.my-namespace:4566/json

This works without additional DNS configuration in client applications.

Amazon EKS Auto Mode does not expose the CoreDNS configuration for the Operator to modify. Set dnsProvider: eksauto on the LocalStack resource to prevent the Operator from trying to update CoreDNS:

spec:
# ... other fields
dnsProvider: eksauto
dnsConfigName: coredns
dnsConfigNamespace: kube-system

The dnsConfigName and dnsConfigNamespace fields remain required by the LocalStack resource schema, but the Operator does not use their values in eksauto mode. Use opt-in endpoint injection to configure workloads that need to access LocalStack.

To persist the LocalStack volume, use the spec.pvcName to specify the PVC you want to mount. This automatically mounts the PVC at /var/lib/localstack.

For example:

# pvc definition
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myPvc
spec:
# ...
# LocalStack instance definition
apiVersion: api.localstack.cloud/v1alpha1
kind: LocalStack
metadata:
name: localstack
namespace: my-namespace
spec:
# ...
pvcName: myPvc

The Operator can be upgraded by applying a newer controller manifest.

Example:

Terminal window
# Install an older version
kubectl apply -f https://github.com/localstack/localstack-operator/releases/download/v0.3.3/controller.yaml
# Upgrade to a newer version
kubectl apply -f https://github.com/localstack/localstack-operator/releases/download/v0.4.1/controller.yaml

Kubernetes will handle rolling updates of the Operator deployment.

To verify that the Operator and LocalStack instance are running:

Terminal window
kubectl get pods -n my-namespace
kubectl get localstacks -n my-namespace

Ensure that:

  • The Operator controller pod is running
  • The LocalStack resource reports a healthy status
  • The LocalStack Service is created
Was this page helpful?