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.
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
docker pull amazon/dynamodb-local Step 2: Run the Container
For basic usage:
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:
# 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:
# 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:
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.
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
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:
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:
# 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)
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)
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)
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:
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
-inMemoryflag - Use the AWS CLI to delete and recreate your tables:
# 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:
# .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
- Use Docker Compose to start both your application and DynamoDB Local together
- Create a startup script that initializes tables and sample data
- Use environment variables to switch between local and AWS endpoints
- Consider using
.aws/configprofiles for different environments
Data and Schema Management
- Version control your table schemas and initialization scripts
- Create seed data for testing different scenarios
- Use the same table structure locally as in production
Testing
- Reset the database before each test suite
- Run tests in parallel using different table names or databases
- 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: