Skip to content

Bring Your Own Cloud (BYOC)

In the Bring Your Own Cloud (BYOC) model, you run the Stately data plane container next to your services, and that data plane container directly talks to DynamoDB in the same account. Stately Cloud doesn’t have any access to your AWS account (even through the data plane)—your DynamoDB table is entirely under your control and data cannot leave your account without you taking action to allow it. The data plane containers connect to the hosted StatelyDB control plane to read information about your schema versions and to coordinate data maintenance tasks.

Unlike the serverless, hosted option, the BYOC deployment model does require more setup than a single click.

First, make sure you have the AWS CLI installed, and that you’ve logged in to the correct region and account. Then download create-table.sh, make it executable, and run it. The table name doesn’t matter—choose whatever you want. This will use CloudFormation to create your table.

curl https://docs.stately.cloud/create-table.sh \
-o ./create-table.sh && chmod a+x ./create-table.sh
./create-table.sh mycooltable

Alternately, you can download the CloudFormation template and use it directly.

You’ll need to make sure that the data plane has the ability to talk to DynamoDB. We recommend attaching a StatelyDB specific permissions policy to your service’s IAM Role. The minimum requirements are:

aws iam create-policy --policy-name StatelyDBDynamoReadWriteAccess \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"dynamodb:*Item",
"dynamodb:Describe*",
"dynamodb:List*",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource" : "*"
}]}'

Visit the console and click “New Store”. Fill in the name and description, then select the “Bring Your Own Cloud - You’ve already created a table in your AWS account” option and enter the full ARN of the DynamoDB table you created.

Provision an Access Key for the Data Plane

Section titled “Provision an Access Key for the Data Plane”

In the console, click “Access Keys” and click “New Access Key”. Choose the “Data Plane Key for BYOC” option, and then save the key somewhere. You’ll need it in the next step.

Add the StatelyDB Data Plane to your service deployment

Section titled “Add the StatelyDB Data Plane to your service deployment”

Now you need to run the StatelyDB data plane container next to your service. We have examples for Kubernetes (EKS) and AWS ECS:

The most straightforward way to deploy the StatelyDB dataplane in Kubernetes is as a sidecar in your existing service pods. Each of your service pods will have a private StatelyDB instance they can talk to. The data plane sidecar then talks to DynamoDB on your behalf. You can see an example deployment YAML here - the important part is the “statelydb” container and its configuration:

deployment.yaml
containers:
... your other containers
- name: statelydb
image: public.ecr.aws/stately/dataplane:latest
ports:
- containerPort: 3030
name: h2c # HTTP/2 cleartext
env:
# Read the data plane access key from a k8s secret.
- name: STATELY_ACCESS_KEY
valueFrom:
secretKeyRef:
name: statelydb-secret
key: STATELY_ACCESS_KEY
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "256Mi"
livenessProbe:
httpGet:
path: /health
port: 3030
initialDelaySeconds: 5
periodSeconds: 10

You can choose the memory and CPU limits depending on your workload - StatelyDB can work well with a relatively small amount of memory, but you’ll want to tweak this depending on your workload. The most important bit is passing in the STATELY_ACCESS_KEY environment variable - you can use Kubernetes secrets or whatever other secrets management system you like. This should be set to the access key you generated above.

You also must make sure that your pod has access to AWS credentials, and that its role has the permissions listed above. EKS Pod Identity is a great way to do that, as it binds an IAM role directly to a pod. Keep in mind that if you use a different way of providing IAM role or user credentials to your pod, you may need to set a REGION environment variable on the StatelyDB container to the current AWS region so it knows where it’s running.

If you use ECS instead of Kubernetes, the setup is still pretty similar. You’ll deploy the StatelyDB data plane container next to your service container in the same task, so each service container gets its own private instance of the data plane. Then the data plane container talks directly to DynamoDB. You can see an example task definition here - the important part is the “statelydb” container and its configuration:

task-definition.json
{
...
"containerDefinitions": [
... your service container ...
{
"name": "statelydb",
"image": "public.ecr.aws/stately/dataplane:latest",
"essential": true,
"portMappings": [
{
"containerPort": 3030,
"protocol": "tcp"
}
],
"environment": [],
"secrets": [
{
"name": "STATELY_ACCESS_KEY",
"valueFrom": "arn:aws:ssm:us-west-2:509869530682:parameter/statelydb/access_key"
}
],
"memoryReservation": 256,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-create-group": "true",
"awslogs-group": "mycoolservice",
"awslogs-region": "us-west-2",
"awslogs-stream-prefix": "statelydb"
}
}
}
]
}

You must make sure that your task role (in taskRoleArn) has the policy you created earlier attached to it. You also need to provide the STATELY_ACCESS_KEY you created earlier to the container - in this example we’ve used the AWS Systems Manager Parameters Store to manage the key.

Now you configure the StatelyDB SDK in your service code. Since the StatelyDB data plane is running in a local container, you need to set the endpoint to http://localhost:3030. You should also turn off authentication since there’s no need for the client to authenticate to a local data plane.

11 collapsed lines
package main
import (
"context"
// The StatelyDB SDK
"github.com/StatelyCloud/go-sdk/stately"
// This is the code you generated from schema
"github.com/StatelyCloud/stately/go-sdk-sample/schema"
)
// Create a client for interacting with a Store.
func makeClientBYOC() stately.Client {
// Use the generated "NewClient" func in your schema package.
dataClient, err := schema.NewClient(
context.TODO(),
12345, // Store ID
&stately.Options{
Endpoint: "http://localhost:3030",
NoAuth: true,
},
)
if err != nil {
panic("failed to build StatelyDB client: " + err.Error())
}
return dataClient
}

The StatelyDB data plane container produces structured logs per request to stdout. It also can produce OpenTelemetry (OTEL) compatible metrics and traces to help with debugging issues - let us know if you’re interested in consuming these into your own metrics and tracing system.

Of course, your DynamoDB table will produce metrics that you can view and alarm on using CloudWatch—there’s no difference there from running your own table.