Skip to content
Free Spinifex sandbox. Create a sandbox Your Spinifex account is live. Access your console
All posts
Engineering · 8 min read

From LocalStack to Production: What Changes When Your AWS Code Runs for Real

Endpoints, IAM, EC2, security groups and state: what an emulator hides and what your own hardware enforces

Moving AWS code from LocalStack, Floci or Moto to production on your own hardware. The endpoint and TLS changes, IAM enforcement, real VMs and service coverage.

TN

Tom Newton

Forward Deployed Engineer

Your Terraform applies cleanly against LocalStack. Your boto3 tests pass against Floci or Moto. The code is written, the CI pipeline is green, and now it has to run somewhere real. For most teams that place is an AWS region. There is another option: the same AWS APIs on hardware you own.

This guide walks through what changes when the same AWS code moves from an emulator to Spinifex, an AWS-compatible cloud that runs on your own servers. We go over the configuration to edit and the services you should check first.

The differences

The code stays the same. All you change is the endpoint, credentials and TLS configuration, and the emulation becomes real: EC2 instances boot as virtual machines, security groups filter traffic, resources persist until you delete them, and IAM policies are evaluated on every request.

Emulator (LocalStack, Floci, MiniStack, Moto) Spinifex
Endpoint One HTTP port, 4566 by convention AWS gateway on 9999, S3 on 8443, both over TLS
Credentials Any key works, conventionally test Real access keys, verified with SigV4
IAM Off by default; every call is allowed Policies evaluated on every non-root request
EC2 Records in memory, or Docker containers KVM virtual machines booted from AMIs
Networking Security groups stored, not enforced Security groups enforced in the hypervisor datapath
State Gone on restart unless persistence is enabled Persists until you delete it

Check service coverage first

Emulators are broad. Floci covers 47 services and MiniStack more than 60, because emulating an API is simpler than running the services behind it. A production platform has to implement each service for real, so the list is shorter.

Spinifex serves EC2, EBS, VPC, S3, IAM, STS, ELB, EKS, ECS, ECR, RDS and ACM. It does not serve Lambda, DynamoDB, SQS or SNS. The full list of supported services and operations is on our AWS API coverage page, generated from the gateway on every build.

Endpoints and TLS

An emulator listens on one plain HTTP port. Spinifex splits the work across two TLS endpoints: the AWS gateway on port 9999 for EC2, IAM, STS, EKS, ECS and the rest, and Predastore, the S3 service, on port 8443. S3 uses path-style addressing, which most emulator configurations already set.

If you use tflocal or a hand-written LocalStack provider block, the Spinifex version is shorter:

provider "aws" {
  region = var.region

  endpoints {
    ec2 = var.spinifex_endpoint
    iam = var.spinifex_endpoint
    sts = var.spinifex_endpoint
    s3  = var.predastore_endpoint
  }

  s3_use_path_style           = true
  skip_credentials_validation = true
  skip_requesting_account_id  = true
  skip_metadata_api_check     = true
  skip_region_validation      = true
}

The access_key = "test" lines go away, and the provider reads real keys from your AWS profile instead. Keep the two credential skip flags when the configuration manages S3: without them, AWS provider 6.x reads bucket tags through S3 Control, whose hostname carries your account ID and cannot resolve against an IP address.

Spinifex generates its own certificate authority at install time. Point every SDK and the AWS CLI at it once, with export AWS_CA_BUNDLE=/etc/spinifex/ca.pem, and no application code needs to know about it. We cover the Terraform details, including AMI lookups and passing endpoints through CI, in Terraform on premise.

IAM policies are enforced

This is an important one. LocalStack leaves IAM enforcement off unless you set ENFORCE_IAM=1, and Moto does not evaluate policies by default, so code that has only run against an emulator has never been told no.

Spinifex verifies and evaluates every request the way AWS does. Each call is checked against the caller's identity policies, group policies and any role it has assumed, an explicit Deny wins, and anything not allowed is denied. The first deploy surfaces every missing iam:PassRole, every role that cannot be assumed, and every policy written with a wildcard that the application stopped needing.

Note: the root user bypasses policy evaluation, on Spinifex as on AWS. If you deploy with root keys, IAM looks like it is working because nothing is being checked. Create an IAM user or role for each pipeline and workload, and use root only to create them.

EC2 instances are virtual machines

In Moto and LocalStack's mock mode, RunInstances returns an instance ID and stores a record. LocalStack's paid tiers can back instances with Docker containers. Either way, your Terraform has been creating something that answers DescribeInstances and boots nothing.

On Spinifex each instance is a KVM virtual machine booted from an AMI. User data runs through cloud-init, instances take seconds to boot rather than milliseconds, and an AMI lookup needs to find a real image. Spinifex publishes its images under the placeholder owner account 000000000000, so data "aws_ami" filters that name an AWS account ID, such as Canonical's, need the owner changed.

Workloads that read the instance metadata service meet one more change. Spinifex serves IMDSv2 only, with session tokens required, and the setting cannot be relaxed. Current AWS SDKs use IMDSv2 already. Scripts that call curl http://169.254.169.254/latest/meta-data/ without a token need updating, and they need the same change on Amazon Linux 2023, which requires IMDSv2 by default.

Security groups filter real traffic

An emulator stores your security group rules and returns them when asked. Nothing sits in the packet path. Spinifex enforces security groups as stateful firewall rules in the hypervisor's virtual switch, before traffic reaches the guest, in the same place AWS enforces them on its Nitro cards.

State persists, so tests need to clean up

Emulators start empty. LocalStack keeps nothing between restarts unless persistence is enabled, and most CI pipelines rely on that. Every run gets a fresh account, and teardown is docker stop.

On Spinifex, volumes, buckets, instances and IAM entities exist until something deletes them. Integration tests that used to leak resources now leak real disk and real compute. Give each pipeline its own account, destroy with terraform destroy rather than relying on a restart, and treat Terraform state as production state from the first apply.

Keep the emulator

None of this replaces the emulator. Floci, MiniStack and Moto remain the right tools for unit tests and fast CI, and we compare them in LocalStack alternatives. The emulator makes sure your code calls the API correctly. Spinifex makes sure your infrastructure works: whether the policies grant enough, the instances boot, and the network lets the right traffic through.

An effective pipeline uses both. Run unit tests against an emulator on every commit, then apply the same Terraform to a Spinifex cluster before production, or as production. The provider block above is the only file that differs between them.

Try the move without hardware

The free sandbox gives you a Spinifex endpoint, access keys and a console for 72 hours, which is enough to point your Terraform configuration at it and see what IAM and the network make of it. The source is on GitHub under AGPL-3.0, and the installation guide and Terraform workbooks are at docs.mulgadc.com.

Frequently asked questions

Can I use LocalStack in production?

No. LocalStack emulates the AWS API for development and CI. IAM enforcement is off by default, EC2 instances are records or Docker containers, and security groups are stored rather than enforced. Production needs AWS itself or a platform that implements those services for real, such as Spinifex on your own hardware.

What changes when I move Terraform from LocalStack to a real AWS-compatible endpoint?

The resources stay the same. The provider block changes: the endpoints point at the real service, the placeholder test credentials are replaced with real access keys, and TLS is verified against the platform's certificate authority. Against Spinifex, the AWS gateway runs on port 9999 and S3 on port 8443.

Why does my code get AccessDenied after moving off an emulator?

Emulators allow every call by default, so policies are never tested. A real platform evaluates the caller's policies on every request, an explicit Deny wins, and anything not allowed is denied. Missing iam:PassRole grants and roles that cannot be assumed are the most common first failures.

Which AWS services does Spinifex support?

Spinifex serves EC2, EBS, VPC, S3, IAM, STS, ELB, EKS, ECS, ECR, RDS and ACM on your own servers. It does not serve Lambda, DynamoDB, SQS or SNS, so applications built on those need another home for those components.

Should I stop using an emulator once I run Spinifex?

No. Floci, MiniStack and Moto stay the fastest way to run unit tests and CI. Use the emulator to check that code calls the API correctly, and apply the same Terraform to a Spinifex staging account to check that policies, instances and networking work.

#LocalStack#AWS Emulator#Terraform#IAM#Spinifex