Learning AWS Without an AWS Account: Running S3, DynamoDB, and Lambda Locally
Image: Local AWS architecture using Floci, Docker, and AWS CLI.
For a long time, I approached AWS the same way many beginners do.
I would read documentation, watch tutorials, and try to memorize services.
The problem was simple:
Reading about cloud services is very different from actually using them.
At the same time, I didn't want to create resources in a cloud account while learning basic concepts.
So I started looking for a way to experiment locally.
That's how I came across Floci, an open-source AWS emulator that exposes AWS-compatible APIs on a local machine.
The idea is surprisingly simple:
Instead of sending requests to AWS, send them to a local container that behaves like AWS.
Why Learn AWS Locally?
When learning cloud concepts, most beginners want to answer questions like:
- What is S3 really used for?
- How does DynamoDB store data?
- What does Lambda actually execute?
- What role does AWS CLI play?
These questions are easier to answer by creating resources than by reading definitions.
Local emulation makes that possible without worrying about cloud costs.
High-Level Architecture
The setup looked like this:
AWS CLI
↓
localhost:4566
↓
Floci Container
├── S3 Emulator
├── DynamoDB Emulator
└── Lambda Emulator
Instead of communicating with AWS infrastructure, AWS CLI communicates with Floci running on the local machine.
Running Floci with Docker
The first step was starting the emulator.
A simple Docker Compose configuration was enough:
services:
floci:
image: floci/floci:latest
ports:
- "4566:4566"
Then:
docker compose up -d
Port 4566 became the endpoint through which AWS CLI communicated with Floci.
Understanding AWS CLI
AWS CLI stands for:
Amazon Web Services Command Line Interface
It acts as a client that converts commands into AWS API requests.
For example:
aws s3 ls
can be viewed conceptually as:
AWS CLI
↓
AWS API Request
↓
S3 Service
Normally those requests go to AWS.
With Floci, the destination changes:
--endpoint-url=http://localhost:4566
This tells AWS CLI to communicate with the local emulator instead.
Learning S3
What Is S3?
S3 is an object storage service.
A useful mental model is:
Bucket
├── resume.pdf
├── image.png
└── hello.txt
A bucket acts as a container.
Everything stored inside it is called an object.
Creating a Bucket
aws --endpoint-url=http://localhost:4566 \
s3 mb s3://notes-app-bucket
Uploading a File
echo "Hello from AWS Learning" > hello.txt
aws --endpoint-url=http://localhost:4566 \
s3 cp hello.txt s3://notes-app-bucket/
Listing Objects
aws --endpoint-url=http://localhost:4566 \
s3 ls s3://notes-app-bucket
Downloading an Object
aws --endpoint-url=http://localhost:4566 \
s3 cp s3://notes-app-bucket/hello.txt .
Key Insight
S3 stores:
- Images
- Videos
- Documents
- Backups
- Application assets
S3 is not a database.
It is object storage.
Learning DynamoDB
What Is DynamoDB?
DynamoDB is a NoSQL database service.
Instead of storing files, it stores records.
Example:
{
"noteId": "1",
"title": "Learning AWS",
"content": "Today I learned S3"
}
Creating a Table
aws --endpoint-url=http://localhost:4566 \
dynamodb create-table \
--table-name Notes \
--attribute-definitions \
AttributeName=noteId,AttributeType=S \
--key-schema \
AttributeName=noteId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
Inserting Data
aws --endpoint-url=http://localhost:4566 \
dynamodb put-item \
--table-name Notes \
--item '{
"noteId":{"S":"1"},
"title":{"S":"Learning AWS"},
"content":{"S":"Today I learned S3 and DynamoDB"}
}'
Retrieving Data
aws --endpoint-url=http://localhost:4566 \
dynamodb get-item \
--table-name Notes \
--key '{"noteId":{"S":"1"}}'
Key Insight
A useful distinction is:
S3
↓
Stores Files
versus
DynamoDB
↓
Stores Records
Understanding this difference made both services much easier to reason about.
Learning Lambda
What Is Lambda?
Lambda is a serverless compute service.
Unlike S3 or DynamoDB, Lambda does not primarily store data.
Its job is to execute code.
Example:
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": "Hello from Lambda"
}
[Insert Figure 4: Lambda Execution Flow Here]
Packaging the Function
Lambda expects a deployment artifact.
The simplest option is a ZIP file.
zip function.zip handler.py
The ZIP package contains:
- Application code
- Supporting files
- Dependencies
Creating the Function
aws --endpoint-url=http://localhost:4566 \
lambda create-function \
--function-name HelloLambda \
--runtime python3.11 \
--handler handler.lambda_handler \
--zip-file fileb://function.zip \
--role arn:aws:iam::000000000000:role/lambda-role
The First Failure
The first invocation failed with:
Failed to start Lambda container
This turned out to be a Docker issue.
Lambda execution inside Floci relies on runtime containers.
Floci needed access to Docker itself.
Fixing the Issue
The solution was mounting Docker's socket:
services:
floci:
image: floci/floci:latest
ports:
- "4566:4566"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Once Floci could communicate with Docker, Lambda executed successfully.
Invoking Lambda
aws --endpoint-url=http://localhost:4566 \
lambda invoke \
--function-name HelloLambda \
response.json
Result:
{
"statusCode": 200,
"body": "Hello from Lambda"
}
A Brief Note on IAM
One interesting observation was that Lambda creation succeeded even though no real IAM role was created.
The emulator is intentionally more forgiving than AWS.
In real AWS, IAM is responsible for controlling:
- Who can access resources
- Which actions are allowed
- Which services can communicate
A Lambda execution role acts as the identity under which the function runs.
Even simple functions require one because AWS needs to know what permissions the function should have if it later interacts with services such as S3 or DynamoDB.
What This Exercise Taught Me
By the end of the experiment, the following concepts became much clearer:
S3
- Creating buckets
- Uploading objects
- Downloading objects
- Listing objects
DynamoDB
- Creating tables
- Inserting records
- Querying records
- Understanding primary keys
Lambda
- Packaging code
- Deploying functions
- Executing code
- Troubleshooting runtime issues
Infrastructure Concepts
- Docker containers
- Service emulation
- Runtime environments
- Deployment artifacts
- IAM fundamentals
Final Mental Model
When I started, S3, DynamoDB, and Lambda felt like unrelated AWS services.
After running them locally, a much simpler mental model emerged:
AWS CLI
↓
Floci
├── S3 → Stores Files
├── DynamoDB → Stores Records
└── Lambda → Runs Code
Sometimes the fastest way to understand cloud services isn't reading more documentation.
It's building a small environment, creating resources, breaking things, fixing them, and observing how the pieces fit together.



Top comments (0)