Developer How-To Guide
This guide describes how to create and manage AWS RDS resources with Klutch, using workflows that mirror familiar Kubernetes objects (for example, Deployments). It is intended for application developers who need practical, example-driven instructions for provisioning and operating Klutch-backed AWS RDS managed databases.
This guide covers:
- Provision an Instance — YAML example, required fields, and recommended best practices.
- Creating a Service Binding — how to create and use bindings and what the generated secret contains.
- Inject Secret to Applications — how to inject the binding secret into a deployment.
- Upgrade and Scale — concise guidance for engine upgrades, resource scaling.
- Configure Parameter Groups — tune PostgreSQL settings using
spec.config.parameterGroup. - Backup and Restore — manual backups, scheduled backups, and restore workflows.
- Cleanup - safe deletion
Prerequisites
Before starting, ensure the following:
- kubectl is installed and configured to access your application cluster.
- All required Klutch RDS prerequisites have been completed.
Provision an Instance
To provision an AWS RDS service instance, create a Service Instance Claim to your Kubernetes cluster. The following template can be used as a starting point:
apiVersion: rds.aws.anynines.com/v1
kind: <ResourceType>
metadata:
name: my-resource
spec:
serviceVersion: # e.g. "17.5"
plan: # e.g. "single-nano"
Both serviceVersion and plan are required fields, and additional optional fields can be used to
customize the instance. This guide uses PostgreSQL as the example, which is currently the only
supported service; for a full list of optional fields, refer to the
PostgreSQL managed database reference.
Below is the PostgreSQL reference example to illustrate the workflows:
cat <<'EOF' > basic-instance-example.yaml
apiVersion: rds.aws.anynines.com/v1
kind: PostgresqlInstance
metadata:
name: example-basic-instance
spec:
serviceVersion: "17.5"
plan: "single-nano"
EOF
Apply the above YAML manifest to create a PostgreSQL instance on AWS:
kubectl apply -f basic-instance-example.yaml
After applying the manifest, please allow some time for the remote resources to be deployed.
Check that the instance was created successfully:
kubectl get postgresqlinstances.rds.aws.anynines.com
Example output:
NAME SYNCED READY CONNECTION-SECRET AGE
example-basic-instance True True 10s
The CONNECTION-SECRET field will remain empty because Klutch uses Service
Bindings to generate connection secrets, rather than relying on Crossplane’s
default secret creation mechanism.
If you want to see more details about the resource (e.g. the actual name of instance on AWS) you can use the command below and look for the status field.
kubectl describe postgresqlinstances.rds.aws.anynines.com example-basic-instance
Create a Service Binding
The ServiceBinding Custom Resource provides a way to start using the database.
When you create a service binding:
- A new database user is automatically created.
- The user credentials and other connection information are stored in a Kubernetes Secret.
Each time a new service binding is created, a new database user is generated and the corresponding connection details are stored in its own secret.
To target a specific service instance, set the spec.instanceRef field in service binding resource manifest.
Optional: If you've followed the example from the previous step, where we created a PostgreSQL Kubernetes Object, you can now proceed to apply the yaml manifest provided in the Example tab below.
- Template
- Example
apiVersion: rds.aws.anynines.com/v1
kind: ServiceBinding
metadata:
name: <name>
namespace: <namespace>
spec:
instanceRef: <postgresql-instance-name>
serviceInstanceType: # e.g. postgresql
Deploy the resource:
kubectl apply -f <your-file-name>
cat <<'EOF' > service-binding-example.yaml
apiVersion: rds.aws.anynines.com/v1
kind: ServiceBinding
metadata:
name: example-basic-instance-sb
namespace: default
spec:
instanceRef: example-basic-instance
serviceInstanceType: postgresql
EOF
Deploy the resource:
kubectl apply -f service-binding-example.yaml
Once the ServiceBinding becomes ready (typically within seconds), a secret is automatically
created in the same namespace. The secret is named {service-binding-name}-service-binding
and contains the database credentials required to access the instance. You can view the
credentials by describing the secret using:
kubectl get secret example-basic-instance-sb-service-binding -o yaml
Example output:
apiVersion: v1
data:
username: <base64 encoded value>
password: <base64 encoded value>
dbname: <base64 encoded value>
endpoint: <base64 encoded value>
...
Inject Secret to Application Deployment
After creating the ServiceBinding resource, a secret containing the database connection details is automatically generated. The next step is to reference these secret fields in your application so your application can connect to the database.
An example deployment manifest demonstrating how to use the secret fields as environment variables is shown below:
apiVersion: apps/v1
kind: Deployment
...
spec:
template:
spec:
containers:
- name: my-app
image: my-app-image
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: <service-binding-name>-service-binding
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: <service-binding-name>-service-binding
key: password
- name: DB_NAME
valueFrom:
secretKeyRef:
name: <service-binding-name>-service-binding
key: dbname
- name: DB_ENDPOINT
valueFrom:
secretKeyRef:
name: <service-binding-name>-service-binding
key: endpoint
...
Upgrade and Scale
Once the database instance is ready, you can update its configuration to upgrade or scale the service. Configuration changes are forward-only: resources can be increased, but not reduced.
You may update different configuration fields independently. For example, you can:
- upgrade the service plan (e.g., move from a single-instance plan to a clustered plan), and/or
- increase the allocated storage size.
These changes are not coupled; storage can be increased without changing the plan, and the plan can be changed without modifying storage. The example below demonstrates updating both fields, but other configuration fields can also be updated.
Below is an example showing how to update the plan and increase the allocated storage size:
apiVersion: rds.aws.anynines.com/v1
kind: PostgresqlInstance
metadata:
name: example-basic-instance
spec:
serviceVersion: "17.5"
plan: "cluster-nano"
config:
compute:
allocatedStorage: 40
replicas:
count: 1
For detailed information on which fields are available to upgrade or scale, see the PostgreSQL managed database reference.
Configure Parameter Groups
You can tune PostgreSQL runtime settings using spec.config.parameterGroup on the
PostgresqlInstance resource.
- Template
- Example
apiVersion: rds.aws.anynines.com/v1
kind: PostgresqlInstance
metadata:
name: <instance-name>
spec:
serviceVersion: "17.5"
plan: "single-small"
config:
parameterGroup:
maxConnections: <integer>
sharedBuffers: <mib>
additionalParameters:
- name: <parameter-name>
value: "<value>"
applyMethod: immediate
cat <<'EOF' > parameter-group-example.yaml
apiVersion: rds.aws.anynines.com/v1
kind: PostgresqlInstance
metadata:
name: example-parameter-group
spec:
serviceVersion: "17.5"
plan: "single-small"
config:
parameterGroup:
maxConnections: 300
sharedBuffers: 512
additionalParameters:
- name: log_temp_files
value: "10240"
applyMethod: immediate
EOF
kubectl apply -f parameter-group-example.yaml
kubectl get postgresqlinstances.rds.aws.anynines.com example-parameter-group -o yaml
Supported typed parameter fields:
| Field | Description | Input unit / values | AWS parameter name |
|---|---|---|---|
maxConnections | Maximum number of concurrent connections | integer, 5-65535 | max_connections |
idleInTransactionSessionTimeout | Idle transaction timeout | milliseconds, >= 0 | idle_in_transaction_session_timeout |
statementTimeout | Statement execution timeout | milliseconds, >= 0 | statement_timeout |
sharedBuffers | PostgreSQL shared buffers | MiB, >= 1 | shared_buffers |
workMem | PostgreSQL work memory | MiB, >= 1 | work_mem |
maintenanceWorkMem | PostgreSQL maintenance work memory | MiB, >= 1 | maintenance_work_mem |
effectiveCacheSize | Planner cache size estimate | MiB, >= 1 | effective_cache_size |
maxWalSenders | Maximum WAL sender processes | integer, >= 0 | max_wal_senders |
maxReplicationSlots | Maximum replication slots | integer, >= 0 | max_replication_slots |
walKeepSize | WAL retained for standbys | MB, >= 0 | wal_keep_size |
logConnections | Log successful connections | boolean | log_connections |
logDisconnections | Log disconnected sessions | boolean | log_disconnections |
logMinDurationStatement | Minimum duration before statement logging | milliseconds, >= -1 | log_min_duration_statement |
rdsForceSSL | Require SSL connections | boolean | rds.force_ssl |
rdsLogicalReplication | Enable logical replication | boolean | rds.logical_replication |
rdsLogRetentionPeriod | Retain PostgreSQL logs in RDS | minutes, 1-10080 | rds.log_retention_period |
Notes:
additionalParametersis the escape hatch for parameters not exposed as typed fields.- Each
additionalParametersentry requiresnameandvalue, and supports optionalapplyMethod(immediateorpending-reboot). - Memory-related typed fields are entered in MiB and translated to the units expected by AWS.
This table is a quick reference for common parameters. For the canonical schema, complete field set, and validation rules, see PostgreSQL managed database fields.
Backup and Restore
Klutch supports both manual and scheduled backups, and two restore modes (from backup and point-in-time restore).
Create Backups
- Manual Backup
- Scheduled Backup
cat <<'EOF' > backup-claim.yaml
apiVersion: rds.aws.anynines.com/v1
kind: Backup
metadata:
name: example-basic-instance-backup
namespace: default
spec:
instanceRef: example-basic-instance
instanceType: "postgres"
EOF
kubectl apply -f backup-claim.yaml
kubectl get backups.rds.aws.anynines.com -n default
cat <<'EOF' > scheduled-backup.yaml
apiVersion: rds.aws.anynines.com/v1
kind: ScheduledBackup
metadata:
name: example-basic-instance-scheduled
namespace: default
spec:
instanceRef: example-basic-instance
instanceType: postgresql
schedule: "09:46-10:16"
retentionDays: 7
EOF
kubectl apply -f scheduled-backup.yaml
kubectl get scheduledbackups.rds.aws.anynines.com -n default
Restore From a Backup
Use one of backupName or backupARN.
fromBackup.plan is required for restore-from-backup operations and must be equal to
or greater than the plan of the source primary instance.
- Using backupName
- Using backupARN
cat <<'EOF' > restore-from-backup.yaml
apiVersion: rds.aws.anynines.com/v1
kind: Restore
metadata:
name: example-basic-instance-restore
namespace: default
spec:
instanceType: "postgresql"
restoredInstanceName: example-basic-instance-restored
fromBackup:
backupName: example-basic-instance-backup
backupType: "manual"
plan: single-nano
EOF
kubectl apply -f restore-from-backup.yaml
kubectl get restores.rds.aws.anynines.com -n default
cat <<'EOF' > restore-from-backup-arn.yaml
apiVersion: rds.aws.anynines.com/v1
kind: Restore
metadata:
name: example-basic-instance-restore-arn
namespace: default
spec:
instanceType: "postgresql"
restoredInstanceName: example-basic-instance-restored-arn
fromBackup:
backupARN: arn:aws:rds:eu-central-1:123456789012:snapshot:example-basic-instance-snapshot
plan: single-nano
EOF
kubectl apply -f restore-from-backup-arn.yaml
kubectl get restores.rds.aws.anynines.com -n default
Point-In-Time Restore
Use one of latestRestoreTime or restoreTime.
For point-in-time restore (PITR), a restore plan is not required.
- Using latestRestoreTime
- Using restoreTime
cat <<'EOF' > restore-pitr.yaml
apiVersion: rds.aws.anynines.com/v1
kind: Restore
metadata:
name: example-basic-instance-pitr
namespace: default
spec:
instanceType: "postgresql"
restoredInstanceName: example-basic-instance-pitr-restored
pointInTime:
instanceRef: example-basic-instance
latestRestoreTime: true
EOF
kubectl apply -f restore-pitr.yaml
kubectl get restores.rds.aws.anynines.com -n default
cat <<'EOF' > restore-pitr-time.yaml
apiVersion: rds.aws.anynines.com/v1
kind: Restore
metadata:
name: example-basic-instance-pitr-time
namespace: default
spec:
instanceType: "postgresql"
restoredInstanceName: example-basic-instance-pitr-time-restored
pointInTime:
instanceRef: example-basic-instance
restoreTime: "2024-06-01T12:00:00Z"
EOF
kubectl apply -f restore-pitr-time.yaml
kubectl get restores.rds.aws.anynines.com -n default
You can inspect restore status details with:
kubectl get restores.rds.aws.anynines.com <restore-name> -n <namespace> -o yaml
Cleanup
Deleting resources works the same way as with any other Kubernetes object. If you have created both a Service Instance and a Service Binding, you can delete them using the following command:
kubectl delete <resource-type> <resource-name>
It is recommended to delete the resources in the reverse order of creation, first the Service Binding, then the Service Instance.