DEV Community

Cover image for Building Event-Driven Microservices: My LocalStack Journey
3

Building Event-Driven Microservices: My LocalStack Journey

In this blog, you will discover how LocalStack makes developing Lambda-based microservices effortless. Test event-driven architectures locally with S3, SQS, and more.

The Microservices Development Challenge

Last month, I was deep into building an event-driven microservices architecture using AWS Lambda. Picture this: file uploads triggering processing workflows, messages flowing between services, notifications being sent - the whole nine yards of modern serverless architecture.

The problem? Testing this locally was nearly impossible. I was constantly deploying to AWS just to see if my Lambda functions would trigger correctly, if my SQS messages were being processed, or if my S3 events were firing as expected. The feedback loop was painfully slow, and debugging felt like shooting in the dark.

That's when I discovered LocalStack, and it completely revolutionized how I develop event-driven systems.

What is LocalStack? (And Why Microservices Developers Love It)

Imagine having your own personal AWS cloud running right on your laptop. That's essentially what LocalStack is - a clever piece of software that mimics AWS services locally, perfect for testing complex event-driven architectures.

I was skeptical at first. "There's no way Lambda functions will actually trigger from S3 events locally," I thought. However, after using it for several months, I can confidently say it has transformed how I build microservices.

Here's what changed for my development workflow:

  • Testing became instant instead of waiting for deployments
  • I could develop complex event flows offline
  • Debugging Lambda functions has become as easy as debugging any local code
  • No more "deploy and pray" development cycles

Getting Started: My First LocalStack Setup

Setting up LocalStack was surprisingly straightforward. I remember being intimidated by the documentation at first, but it's just a Docker container that pretends to be AWS.

Here's the Docker Compose file I use (and yes, it is this simple):

version: '3.8'

services:
  localstack:
    container_name: my-local-aws
    image: localstack/localstack:4
    environment:
      SERVICES: s3,sqs,sns,ses  # Start with just what you need
      PERSISTENCE: 1            # Keep data between restarts
      DEBUG: 1                  # Helpful for learning

    ports:
      - "4566:4566"  # The magic port where AWS lives locally

    volumes:
      - localstack-data:/var/lib/localstack
      - /var/run/docker.sock:/var/run/docker.sock

volumes:
  localstack-data:
Enter fullscreen mode Exit fullscreen mode

The first time I ran docker-compose up, I felt like a kid on Christmas morning. Within 30 seconds, I had S3, SQS, and SES running on my laptop. No credit card required.

The Services That Power My Event-Driven Architecture

Lambda: The Heart of Microservices

Lambda functions are the core of my event-driven architecture, and LocalStack makes testing them incredibly smooth. I can trigger functions from S3 events, SQS messages, or API Gateway calls - all locally.

The game-changer? I can set breakpoints in my Lambda code and debug it just like any other local application. No more adding print statements and redeploying to see what's happening.

# Your Lambda code works exactly the same
def lambda_handler(event, context):
    # Process S3 event, SQS message, etc.
    return {'statusCode': 200, 'body': 'Success'}
Enter fullscreen mode Exit fullscreen mode

S3: Event-Driven File Processing

S3 events are crucial for microservices that process files. With LocalStack, I can upload a file and immediately see my Lambda function trigger, process the file, and send messages to other services.

The best part? I can test different file types, sizes, and edge cases without worrying about cleanup or storage costs.

SQS: Reliable Inter-Service Communication

SQS is the backbone of my microservices communication. LocalStack lets me test message flows, dead letter queues, and retry logic locally. I can even simulate message failures to test my error handling.

The magic moment was when I realized I could test my entire microservices choreography locally - watching messages flow from service to service in real-time.

Making LocalStack Feel Like Home

The Setup Script That Saved My Sanity

After a few weeks of manually creating the same microservices infrastructure every time I restarted LocalStack, I got smart and wrote a setup script. Now, every time LocalStack starts, it automatically creates my entire event-driven architecture.

Here's my initialization script for a typical microservices setup:

#!/bin/bash
echo "Setting up microservices infrastructure..."

# Create S3 buckets for different services
awslocal s3 mb s3://user-uploads
awslocal s3 mb s3://processed-files
awslocal s3 mb s3://service-logs

# Set up message queues for inter-service communication
awslocal sqs create-queue --queue-name user-events
awslocal sqs create-queue --queue-name file-processing
awslocal sqs create-queue --queue-name notification-queue

# Create SNS topics for pub/sub messaging
awslocal sns create-topic --name user-updates
awslocal sns create-topic --name system-alerts

echo "Microservices infrastructure ready! 🚀"
Enter fullscreen mode Exit fullscreen mode

I put this in ./initialization/aws/init-aws.sh and LocalStack runs it automatically when it starts. It's like having a personal assistant set up your workspace every morning.

Pro Tips I Learned the Hard Way

Start Small: Don't enable every AWS service on day one. I made this mistake and LocalStack took forever to start. Begin with just the services you actually use.

Use Persistence: Add PERSISTENCE: 1 to your Docker Compose file. Trust me, you don't want to lose your test data every time you restart.

Install awslocal: This little CLI tool is a game-changer. Instead of typing aws --endpoint-url=http://localhost:4566, you just type awslocal. Your fingers will thank you.

How LocalStack Revolutionized My Microservices Testing

From "Deploy and Debug" to "Test First"

Before LocalStack, my microservices testing strategy was basically "write unit tests for individual functions and hope the integration works in production." Testing event-driven flows was nearly impossible locally.

Now I can test my entire microservices architecture locally:

  1. Upload a file to S3 (triggers file-processing service)
  2. Lambda processes file and sends message to SQS
  3. Another Lambda picks up the SQS message
  4. Processes data and stores results in DynamoDB
  5. Publishes completion event to SNS
  6. Notification service sends email via SES

All of this happens in seconds, and I can run it as many times as needed to perfect the flow.

The "Gotcha" Moments That LocalStack Catches

Let me tell you about some tricky bugs LocalStack has helped me catch:

The Event Structure Mixup: I once wrote a Lambda function that expected S3 events but was receiving SQS messages. The event structure was completely different, but I only discovered this when testing the full flow locally.

The Permission Puzzle: My Lambda function was failing silently because it didn't have permission to write to a specific S3 bucket. LocalStack's logs showed me the exact permission error immediately.

The Message Format Fiasco: I was sending JSON messages between services, but one service was expecting XML. LocalStack let me trace the entire message flow and spot the mismatch instantly.

Making LocalStack Lightning Fast

The Need for Speed

When I first started using LocalStack, it felt a bit sluggish. Turns out, I was being greedy and enabling every single AWS service. Here's what I learned about making it fast:

Only Enable What You Use: If you're just testing S3 and SQS, don't enable Lambda, DynamoDB, and 15 other services. Your startup time will go from 2 minutes to 20 seconds.

Persistence is Your Friend: Enable persistence so your data survives container restarts. Nothing's more frustrating than losing your test setup because Docker decided to restart.

Give It Enough Memory: LocalStack is doing a lot of heavy lifting. I give it at least 2GB of RAM, and it runs much smoother.

The Gotchas I Wish Someone Had Told Me

Port 4566 is Popular

Apparently, everyone uses port 4566 for something. If LocalStack won't start, check if something else is using that port. I usually just change it to 4567 in my Docker Compose file and move on with my life.

Your Code Needs One Tiny Change

The beautiful thing about LocalStack is that your existing AWS code works with almost no changes. You just need to point it to localhost:4566 instead of the real AWS endpoints:

# Production
s3_client = boto3.client('s3')

# LocalStack (just add endpoint_url)
s3_client = boto3.client('s3', endpoint_url='http://localhost:4566')
Enter fullscreen mode Exit fullscreen mode

Lambda Functions Can Be Tricky

If you're testing Lambda functions, they need Docker to run inside LocalStack. Make sure you mount the Docker socket in your compose file, or your Lambdas will fail silently (and you'll spend hours debugging like I did).

When Things Go Wrong (And They Will)

Debugging Like a Detective

LocalStack is pretty reliable, but when something goes wrong, here's how I debug:

Check the Health: curl http://localhost:4566/_localstack/health tells you which services are running. If S3 shows as "disabled" when you expect it to be "available," you know where to start looking.

Image description

Read the Logs: docker logs my-local-aws -f is your best friend. LocalStack is pretty chatty about what it's doing, especially with DEBUG mode enabled.

List Your Resources: Sometimes I forget what I've created. awslocal s3 ls or awslocal sqs list-queues helps me remember what's actually there.

The Development Speed Boost You'll Love

Instant Feedback Loops: Testing microservices interactions used to take minutes (deploy, test, debug, repeat). Now it takes seconds. I can iterate on my event-driven architecture as fast as I can think.

Complete Offline Development: I can develop my entire microservices stack on a plane, in a coffee shop, or anywhere without internet. The whole AWS ecosystem runs on my laptop.

Fearless Experimentation: Want to try a new event pattern? Test a different message format? Experiment with Lambda triggers? Go ahead! There's no deployment overhead or cleanup required.

Real Integration Testing: I can test the actual integration between services, not just mock it. This catches so many issues that unit tests miss.

Making the Transition Smooth

The Environment Switch Pattern

Here's the pattern I use to seamlessly switch between LocalStack and real AWS:

import os

def get_aws_client(service):
    if os.getenv('ENVIRONMENT') == 'local':
        return boto3.client(
            service,
            endpoint_url='http://localhost:4566',
            aws_access_key_id='test',
            aws_secret_access_key='test'
        )
    else:
        return boto3.client(service)  # Uses real AWS credentials
Enter fullscreen mode Exit fullscreen mode

Now I can develop locally and deploy to production with the same code. Just change an environment variable.

Keeping Your Data Safe

One thing I learned the hard way: back up your LocalStack data if you're working on something important. I once lost a week's worth of test data when I accidentally deleted my Docker volume.

Now I occasionally run:

docker cp my-local-aws:/var/lib/localstack ./backup
Enter fullscreen mode Exit fullscreen mode

Just in case.

When LocalStack Misbehaves

The "Turn It Off and On Again" Solution

90% of LocalStack issues can be solved with:

docker-compose down -v
docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

The -v flag removes volumes, giving you a completely fresh start. Sometimes LocalStack just needs a clean slate.

The "It Was Working Yesterday" Problem

If LocalStack suddenly stops working, check if Docker is running out of space:

docker system df
Enter fullscreen mode Exit fullscreen mode

If you're low on space, clean up with:

docker system prune
Enter fullscreen mode Exit fullscreen mode

The Lambda Mystery

Lambda functions in LocalStack can be finicky. If they're not working, make sure you've mounted the Docker socket in your compose file. Lambda needs Docker to run, and without that mount, it fails silently.

Why Every Microservices Developer Should Try LocalStack

LocalStack isn't just a tool - it's a development philosophy. It's about building and testing your entire system locally before ever touching the cloud.

Your Next Steps

  1. Start with Lambda + SQS: Copy my Docker Compose file and create a simple event-driven flow
  2. Add S3 Events: Test file upload triggers and processing workflows
  3. Build Your Architecture: Gradually add more services as your microservices grow
  4. Share Your Experience: I'd love to hear how LocalStack changes your microservices development

The Bottom Line

If you're building event-driven microservices with AWS, LocalStack will transform your development experience. It's the difference between hoping your services work together and knowing they do.

Trust me, once you experience the joy of testing complex event flows locally, you'll never go back to deploy-and-pray development.


Have you tried LocalStack? What's been your experience? Drop a comment below - I'd love to hear your stories!

Helpful Links

Runner H image

Automate Your Workflow in Slack, Gmail, Notion & more

Runner H connects to your favorite tools and handles repetitive tasks for you. Save hours daily. Try it free while it’s in beta.

Try for Free

Top comments (0)

đź‘‹ Kindness is contagious

Explore this practical breakdown on DEV’s open platform, where developers from every background come together to push boundaries. No matter your experience, your viewpoint enriches the conversation.

Dropping a simple “thank you” or question in the comments goes a long way in supporting authors—your feedback helps ideas evolve.

At DEV, shared discovery drives progress and builds lasting bonds. If this post resonated, a quick nod of appreciation can make all the difference.

Okay