Run DynamoDB Locally with Docker or the JAR

Get DynamoDB Local running with one docker command, keep data across restarts, and connect from the CLI, Boto3, Node or Java. Plus what Local cannot do.

Orlando Adeyemi 8 min read

Developing applications that use Amazon DynamoDB often requires frequent testing and iteration. Running DynamoDB locally provides a cost-effective, internet-independent environment for development and testing, saving both time and money.

This guide covers everything you need to know about running DynamoDB locally, including setup with Docker and the downloadable JAR, connecting your applications, and important limitations to keep in mind.

What is DynamoDB Local?

DynamoDB Local is a downloadable version of Amazon DynamoDB that lets you develop and test applications using a DynamoDB-compatible database on your computer, without accessing the DynamoDB web service. It’s a perfect solution for:

  • Developing and testing without incurring AWS costs
  • Working offline or in environments with limited internet access
  • Running automated tests in CI/CD pipelines
  • Learning DynamoDB without AWS account requirements

DynamoDB Local provides most of the functionality of the AWS DynamoDB service, with a few important differences we’ll cover later in this guide.

Running DynamoDB Local with Docker

Using Docker is the simplest way to get started with DynamoDB Local. It requires minimal setup and works consistently across different operating systems and environments.

Prerequisites

  • Docker installed on your system

Step 1: Pull the Official DynamoDB Local Image

Shell
docker pull amazon/dynamodb-local

Step 2: Run the Container

For basic usage:

Shell
docker run -p 8000:8000 amazon/dynamodb-local

You should see output indicating that DynamoDB Local is running on port 8000.

Docker with Persistent Data

By default, data in DynamoDB Local is lost when the container stops. To persist your data between sessions:

Shell
# Create a directory for DynamoDB datamkdir -p ~/dynamodb-local-data# Run with a volume mountdocker run -p 8000:8000 -v ~/dynamodb-local-data:/home/dynamodblocal/data amazon/dynamodb-local -jar DynamoDBLocal.jar -dbPath /home/dynamodblocal/data

Docker Compose Example

For more complex setups, you might prefer using Docker Compose:

YAML
# docker-compose.ymlversion: '3.8'services:  dynamodb-local:    image: amazon/dynamodb-local    container_name: dynamodb-local    ports:      - "8000:8000"    volumes:      - ./dynamodb-data:/home/dynamodblocal/data    command: "-jar DynamoDBLocal.jar -dbPath /home/dynamodblocal/data"    working_dir: /home/dynamodblocal

Start it with:

Shell
docker-compose up -d

Running DynamoDB Local with Java JAR

If you prefer not to use Docker, you can download and run the DynamoDB Local JAR file directly.

Prerequisites

  • Java Runtime Environment (JRE) version 8.x or newer

Step 1: Download the DynamoDB Local JAR

Download the latest version from the AWS DynamoDB Local download page.

Shell
curl -O https://d1ni2b6xgvw0s0.cloudfront.net/v2.x/dynamodb_local_latest.tar.gztar -xzf dynamodb_local_latest.tar.gzcd dynamodb_local_latest

Step 2: Run the JAR

Shell
java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb

Additional command-line options:

  • -port <port-number>: Use a specific port (default is 8000)
  • -dbPath <path>: Specify a directory for database files
  • -sharedDb: Use a single database file instead of separate files per credential/region
  • -inMemory: Run in memory (data will be lost when stopped)
  • -optimizeDbBeforeStartup: Run offline optimization before startup

Example with custom port and path:

Shell
java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -port 8080 -dbPath /path/to/data/directory -sharedDb

Connecting to DynamoDB Local

Once DynamoDB Local is running, you’ll need to configure your applications and tools to connect to it instead of the AWS DynamoDB service.

Using AWS CLI

Configure the AWS CLI to use the local endpoint:

Shell
# List tables in local DynamoDBaws dynamodb list-tables --endpoint-url http://localhost:8000# Create a tableaws dynamodb create-table \    --table-name Users \    --attribute-definitions AttributeName=UserId,AttributeType=S \    --key-schema AttributeName=UserId,KeyType=HASH \    --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \    --endpoint-url http://localhost:8000# Put an itemaws dynamodb put-item \    --table-name Users \    --item '{"UserId": {"S": "user123"}, "Name": {"S": "John Doe"}}' \    --endpoint-url http://localhost:8000# Query the tableaws dynamodb scan --table-name Users --endpoint-url http://localhost:8000

Using Python (Boto3)

Python
import boto3# Connect to local DynamoDBdynamodb = boto3.resource('dynamodb', endpoint_url='http://localhost:8000')# Create a tabletable = dynamodb.create_table(    TableName='Users',    KeySchema=[        {            'AttributeName': 'UserId',            'KeyType': 'HASH'        }    ],    AttributeDefinitions=[        {            'AttributeName': 'UserId',            'AttributeType': 'S'        }    ],    ProvisionedThroughput={        'ReadCapacityUnits': 5,        'WriteCapacityUnits': 5    })# Wait until the table existstable.meta.client.get_waiter('table_exists').wait(TableName='Users')# Add an itemtable.put_item(   Item={        'UserId': 'user123',        'Name': 'John Doe',        'Email': 'john@example.com'    })# Query the tableresponse = table.scan()for item in response['Items']:    print(item)

Using Node.js (AWS SDK v3)

JavaScript
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");const {  DynamoDBDocumentClient,  CreateTableCommand,  PutCommand,  ScanCommand} = require("@aws-sdk/lib-dynamodb");// Create DynamoDB clientconst client = new DynamoDBClient({  region: "local",  endpoint: "http://localhost:8000"});const docClient = DynamoDBDocumentClient.from(client);async function main() {  // Create a table  try {    await docClient.send(new CreateTableCommand({      TableName: "Users",      KeySchema: [        { AttributeName: "UserId", KeyType: "HASH" }      ],      AttributeDefinitions: [        { AttributeName: "UserId", AttributeType: "S" }      ],      ProvisionedThroughput: {        ReadCapacityUnits: 5,        WriteCapacityUnits: 5      }    }));    console.log("Table created successfully.");  } catch (err) {    console.error("Error creating table:", err);  }  // Add an item  try {    await docClient.send(new PutCommand({      TableName: "Users",      Item: {        UserId: "user123",        Name: "John Doe",        Email: "john@example.com"      }    }));    console.log("Item added successfully.");  } catch (err) {    console.error("Error adding item:", err);  }  // Query the table  try {    const { Items } = await docClient.send(new ScanCommand({      TableName: "Users"    }));    console.log("Scan results:", Items);  } catch (err) {    console.error("Error scanning table:", err);  }}main();

Using Java (AWS SDK v2)

Java
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;import software.amazon.awssdk.regions.Region;import software.amazon.awssdk.services.dynamodb.DynamoDbClient;import software.amazon.awssdk.services.dynamodb.model.*;import java.net.URI;import java.util.HashMap;import java.util.Map;public class DynamoDBLocalExample {    public static void main(String[] args) {        // Set up DynamoDB client        DynamoDbClient dynamoDbClient = DynamoDbClient.builder()                .endpointOverride(URI.create("http://localhost:8000"))                // The region is required, but irrelevant for local DynamoDB                .region(Region.US_EAST_1)                // Credentials are required but not actually checked for local DynamoDB                .credentialsProvider(StaticCredentialsProvider.create(                        AwsBasicCredentials.create("dummy", "credential")))                .build();        // Create table        try {            CreateTableRequest request = CreateTableRequest.builder()                    .tableName("Users")                    .keySchema(KeySchemaElement.builder()                            .attributeName("UserId")                            .keyType(KeyType.HASH)                            .build())                    .attributeDefinitions(AttributeDefinition.builder()                            .attributeName("UserId")                            .attributeType(ScalarAttributeType.S)                            .build())                    .provisionedThroughput(ProvisionedThroughput.builder()                            .readCapacityUnits(5L)                            .writeCapacityUnits(5L)                            .build())                    .build();            dynamoDbClient.createTable(request);            System.out.println("Table created successfully");        } catch (DynamoDbException e) {            System.err.println("Error creating table: " + e.getMessage());        }        // Add item        try {            Map<String, AttributeValue> item = new HashMap<>();            item.put("UserId", AttributeValue.builder().s("user123").build());            item.put("Name", AttributeValue.builder().s("John Doe").build());            item.put("Email", AttributeValue.builder().s("john@example.com").build());            PutItemRequest request = PutItemRequest.builder()                    .tableName("Users")                    .item(item)                    .build();            dynamoDbClient.putItem(request);            System.out.println("Item added successfully");        } catch (DynamoDbException e) {            System.err.println("Error adding item: " + e.getMessage());        }        // Scan table        try {            ScanRequest scanRequest = ScanRequest.builder()                    .tableName("Users")                    .build();            ScanResponse response = dynamoDbClient.scan(scanRequest);            response.items().forEach(item -> {                System.out.println("Item: " + item);            });        } catch (DynamoDbException e) {            System.err.println("Error scanning table: " + e.getMessage());        }    }}

Testing Your Connection

To verify that DynamoDB Local is running correctly:

Shell
aws dynamodb list-tables --endpoint-url http://localhost:8000

If DynamoDB Local is running, this command should return a JSON response with an empty list of table names (if you haven’t created any tables yet).

Usage Tips for DynamoDB Local

Throughput Considerations

When using DynamoDB Local, throughput settings (ReadCapacityUnits and WriteCapacityUnits) are required but not enforced. You can specify any values, and your operations won’t be throttled.

Data Persistence

  • In-memory mode: Data is lost when DynamoDB Local stops
  • File mode: Data is saved to disk and persists between restarts
  • Shared database mode: All clients use the same database file instead of separate ones per AWS credentials/region

Working with Multiple Applications

You can share a single DynamoDB Local instance among multiple applications by using the -sharedDb flag. This simplifies testing across different components.

Resetting the Database

If you need to start fresh, you can:

  • Delete the data files (if using file mode)
  • Stop and restart the container with the -inMemory flag
  • Use the AWS CLI to delete and recreate your tables:
Shell
# Delete all tablesfor table in $(aws dynamodb list-tables --endpoint-url http://localhost:8000 --query 'TableNames' --output text); do    aws dynamodb delete-table --table-name $table --endpoint-url http://localhost:8000done

Limitations of DynamoDB Local

While DynamoDB Local is excellent for development and testing, it has some important limitations compared to the AWS DynamoDB service:

Features Not Supported

  • DynamoDB Streams: The change data capture feature is not fully supported
  • Global Tables: Multi-region replication is not available
  • Time to Live (TTL): While the API is present, actual automatic expiration doesn’t occur
  • On-demand capacity mode: While the API accepts this setting, it doesn’t function exactly as in AWS
  • AWS Identity and Access Management (IAM): Authentication and authorization are minimal
  • AWS CloudWatch integration: Metrics are not published
  • Some transactional APIs: May have partial implementation

Performance Differences

DynamoDB Local performance depends on your local hardware and isn’t representative of the AWS service’s performance. Don’t use it for performance benchmarking.

Consistency Model

While DynamoDB Local supports the ConsistentRead parameter, its actual consistency behavior might differ from the AWS service since it’s a single-node implementation.

Using DynamoDB Local in CI/CD Pipelines

DynamoDB Local is perfect for integration testing in continuous integration environments. Here’s a simple example for GitHub Actions:

YAML
# .github/workflows/test.ymlname: Testson: [push, pull_request]jobs:  test:    runs-on: ubuntu-latest    services:      dynamodb:        image: amazon/dynamodb-local        ports:          - 8000:8000    steps:      - uses: actions/checkout@v3      - name: Set up Node.js        uses: actions/setup-node@v3        with:          node-version: '18'      - name: Install dependencies        run: npm ci      - name: Run tests        run: npm test        env:          DYNAMODB_ENDPOINT: http://localhost:8000

Best Practices

Local Development Workflow

  1. Use Docker Compose to start both your application and DynamoDB Local together
  2. Create a startup script that initializes tables and sample data
  3. Use environment variables to switch between local and AWS endpoints
  4. Consider using .aws/config profiles for different environments

Data and Schema Management

  1. Version control your table schemas and initialization scripts
  2. Create seed data for testing different scenarios
  3. Use the same table structure locally as in production

Testing

  1. Reset the database before each test suite
  2. Run tests in parallel using different table names or databases
  3. Simulate error conditions by intercepting SDK calls

Conclusion

Running DynamoDB locally provides significant advantages for development and testing, including cost savings, offline capabilities, and faster iteration cycles. Whether you choose Docker or the JAR file approach, DynamoDB Local offers a convenient way to work with a DynamoDB-compatible database without accessing AWS.

Remember to account for the limitations when designing your test strategies, particularly around features that aren’t fully supported in the local version.

Looking for a more powerful way to interact with DynamoDB, both locally and in the cloud? Dynomate provides an intuitive GUI that connects to both DynamoDB Local and AWS DynamoDB, making it easier to visualize your data, design tables, and build queries without writing code.

For more information about working with DynamoDB, check out our related articles:

All posts

10 DynamoDB Advantages and 10 Disadvantages

DynamoDB scales to millions of requests with no DBA, but the 400KB item cap and rigid access patterns bite. Ten upsides, ten tradeoffs, and when to walk.

Reading time: 12 min

DynamoDB Data Types: Every Type, With Examples

The scalar, document, set and binary types DynamoDB supports, how each counts against the 400KB item limit, and the empty-value rules that break writes.

Reading time: 10 min

DynamoDB DAX: How the Cache Works and When to Use It

DAX is a write-through cache that drops DynamoDB reads from milliseconds to microseconds. How the item and query caches work, what it costs, when to skip.

Reading time: 12 min