.NET and AWS S3 with LocalStack: How to develop with local S3 buckets
Simplifying cloud testing with LocalStack
Introduction
LocalStack is an open-source framework that allows us to emulate the major AWS services locally, making it easier to develop and test cloud applications without incurring the cost and complexity of deploying to a real cloud environment.
In this post, I’ll show how to configure it to emulate S3 buckets and how to interact with those buckets from a C# application.
Running LocalStack
LocalStack can be run as a CLI or using a container. In this post, I’ll explain how to run it in a container with docker run and docker-compose.
ℹ️ If you don’t have docker or other container runtime installed, click here for Docker installation instructions or here for Podman installation instructions.
Container
To start a container with a LocalStack instance, run the following command:
docker run --rm -it -p 4566:4566 -p 4510-4559:4510-4559 -e EXTRA_CORS_ALLOWED_ORIGINS=https://app.localstack.cloud. localstack/localstack:1.3.1
Note that it is exposing port 4566 and ports 4510 to 4559 and allowing CORS access from https://app.localstack.cloud. (to allow access from the LocalStack dashboard).
🚨 It’s recommended to use a specific version instead of
latestto avoid problems with new versions updated automatically.
Docker compose
Starting LocalStack from docker compose is just as easy. Just add the localstack service, as below, to a docker-compose.yaml file:
| |
Then run the following command:
docker-compose up
🚨 It’s recommended to use a specific version instead of
latestto avoid problems with new versions updated automatically.
⚠️ I’ve added the environment variable
EXTRA_CORS_ALLOWED_ORIGINSwith the valuehttps://app.localstack.cloud.to allow access from the LocalStack dashboard.
💡 The updated docker-compose.yaml file can be found here, in the LocalStack repo.
LocalStack Dashboard
LocalStack has a web-based dashboard that allows us to manage and configure its services and visualize its logs.
Access https://app.localstack.cloud. and it will connect to the LocalStack instance running locally.
It runs in the browser, so there is no need to expose any ports to the internet.

🚨 If the dashboard says “Please start LocalStack to check the System Status” and the container log shows Blocked CORS request from forbidden origin https://app.localstack.cloud., the
EXTRA_CORS_ALLOWED_ORIGINSenvironment variable was not correctly set tohttps://app.localstack.cloud.. See here.
Interacting with LocalStack using the AWS CLI
We’ll use the AWS CLI to interact with LocalStack. If you don’t have the AWS CLI, look here for instructions on how to install it.
Configuring a profile for LocalStack
The AWS CLI requires credentials when running. LocalStack doesn’t validate the credentials by default, so will create a profile with anything as access key and secret key just to make the CLI happy.
- In a terminal, type
aws configure --profile localstack; - For
AWS Access Key ID [None]:, type anything; - For
AWS Secret Access Key [None]:, type anything; - For
Default region name [None]:, type the region you prefer (for example,us-east-1); - For
Default output format [None]:, typejson.
How to create a bucket using the AWS CLI
To create a S3 bucket in LocalStack, we’ll use the aws s3 mb command (mb is short for Make Bucket).
The command below will create a bucket with the name local-bucket-name using the AWS CLI profile we previously created with the name localstack. It’s important to pass the --endpoint parameter or else it will try to create the bucket in AWS.
aws s3 mb s3://local-bucket-name --endpoint http://localhost:4566 --profile localstack
Looking at LocalStack dashboard we can see the bucket was created:

How to list a bucket contents using the AWS CLI
To look the contents of a bucket, we can use the aws s3 ls command:
aws s3 ls s3://local-bucket-name --endpoint http://localhost:4566 --profile localstack
Or use the LocalStack dashboard:

Accessing LocalStack from .NET
To access an S3 bucket from LocalStack in .NET we use the same libraries we use to access it in AWS.
In this example, I’ll use the AWSSDK.S3 and the AWSSDK.Extensions.NETCore.Setup NuGet packages, both from AWS.
How does the AmazonS3Client get AWS access data?
When running in AWS, the AmazonS3Client will get its access data from the IAM Role attached to the service running it. When running locally, it will get from the AWS CLI profile named default or from the settings we pass to it.
In the code below, I’m checking for a configuration section with the name AWS, that is not present in the production environment. If it’s found, I set the Region, ServiceURL and ForcePathStyle properties of the AmazonS3Config and pass it to the creation of the AmazonS3Client.
Program.cs
| |
AwsExtensions.cs
| |
The appsettings.Development.json has the configurations pointing to the LocalStack instance:
appsettings.Development.json
| |
⚠️ The
ForcePathStyleforces the use ofhttps://s3.amazonaws.com/<bucket-name>/<object-key>styled URLs instead ofhttps://<bucket-name>.s3.amazonaws.com/<object-key>URLs.The
ForcePathStyleneeds to be set totruefor theAmazonS3Clientto work with LocalStack.
Upload an image to the S3 Bucket
Using Minimal APIs, I created an endpoint that receives a file and saves it in the S3 bucket.
The code is straight forward:
Program.cs
| |
Now, we can test it from Postman:

Get an image from the S3 Bucket
I also created an endpoint that returns the file with the key passed by parameter from the S3 bucket:
| |
Testing from Postman, we can see the image previously uploaded:

