Photo by Growtika / Unsplash

Deploying Databases on DigitalOcean with Crossplane

Jubril Oyetunji
Jubril Oyetunji

Table of Contents

While Kubernetes has become the go-to platform for deploying business workloads, a major challenge still lies in managing the resources and services that your applications depend on, which often live outside the Kubernetes cluster.

Crossplane is a cool open-source project that extends the Kubernetes control plane to provision and manage cloud resources across multiple providers. Unlike Terraform, which relies on state files stored externally, Crossplane leverages Kubernetes primitives like custom resources and controllers to manage the desired state of your cloud resources. It continuously reconciles the actual state against the defined specifications, making changes as needed to match the desired state.

In this tutorial, we will go through how you can deploy a DigitalOcean database using Crossplane and Civo as a management cluster

Why Civo for Your Management Cluster?

Aside from simplicity, the biggest benefit of using Civo for your management cluster is cost. Civo's pricing plans are incredibly cost-effective, making it an attractive choice for hosting a management cluster that doesn't necessarily need to be on the same cloud provider as your workloads.

Additionally, hosting your management cluster on a different cloud provider adds redundancy and resilience to your setup. In the event of an outage or issue with your primary cloud provider, you can still access and manage your resources from the separate management cluster.

What are the Benefits of Crossplane?

Now that we understand the benefits of separating your management cluster, let's turn our attention to using Crossplane. Here are some reasons to consider using Crossplane:

Unified Management

Crossplane enables you to manage resources from different cloud providers within your Kubernetes cluster, offering a centralized view of your infrastructure. without jumping between different management consoles or CLI tools!

Familiar Complexity

By leveraging familiar Kubernetes concepts like Custom Resource Definitions (CRDs) and controllers, Crossplane simplifies infrastructure management. If your team is already well-versed in Kubernetes, this can be a huge productivity boost, as you can apply the same mental models and workflows to manage cloud resources.

Continuous Reconciliation

Like Kubernetes itself, Crossplane continuously monitors the desired state of your resources and reconciles any deviations. This GitOps-like approach ensures that your infrastructure stays in sync with your defined configurations, reducing manual interventions and potential misconfigurations. In contrast, Terraform operates through a plan-and-apply cycle, where you define your desired state, review the planned changes, and then apply them. While this allows for a preview of changes before execution, it's a more manual and discrete process. Crossplane's continuous reconciliation model aims to provide a more automated and self-healing approach, automatically correcting drift from the desired state as it occurs, similar to how Kubernetes manages application deployments.


This tutorial assumes some familiarity with Kubernetes. In addition, you will need the following to continue:

Creating a Management Cluster

We’ll begin by firing up our management cluster using the Civo CLI:

civo k3s create --create-firewall --nodes 1 -m --save --switch --wait do-crossplane 

This will provision a one-node Kubernetes cluster, to verify your cluster was created successfully run, kubectl get nodes

Output is similar to:

NAME                                             	STATUS   ROLES	AGE 	VERSION
k3s-do-crossplane-c0cf-22cf12-node-pool-70a6-hywcj   Ready	<none>   3m24s   v1.28.7+k3s1

Installing Crossplane

Much like Terraform, Crossplane has a concept of providers. Providers enable Crossplane to provision infrastructure on an external service.

Begin by installing Crossplane:

# create a namespace
kubectl create namespace crossplane-system

# add helm chart
helm repo add crossplane-stable
helm repo update

# install crds
helm install crossplane --namespace crossplane-system crossplane-stable/crossplane

You can verify  the installation was a success by looking at the pods in the Crossplane Namsespace:

 kubectl get pods -n crossplane-system

Output is similar to:

NAME                                   	READY   STATUS	RESTARTS   AGE
crossplane-rbac-manager-747cbc499b-hjfvz   1/1 	Running   0      	4m52s
crossplane-d577689b8-ss92k             	1/1 	Running   0      	4m52s

With Crossplane deployed, we can install the Digital Ocean provider. This will give us access to custom resources for deploying our database. Apply the following manifest to your cluster to install the Digital Ocean provider:

kubectl apply -f - <<EOF
kind: Provider
  name: provider-digitalocean

Finally, we will need to tell our cluster about the custom resources DigitalOcean provides, this isn’t bundled in with the provider install as such, we will need to source them from the Crossplane repo directly.

Verify the provider is installed correctly by running:

kubectl get provider

⚠️ Note: Providers are a Crossplane-specific concept and are installed as part of the custom resource definitions.

Output is similar to:

Clone the repo:

git clone && cd provider-digitalocean

Apply the CRDs:

 kubectl apply -f package/crds

Configuring the DigitalOcean Crossplane Provider

Before interacting with DigitalOcean resources, we need to provide credentials to the Crossplane provider. This involves creating a Kubernetes Secret to store your DigitalOcean API token securely.

⚠️ For this demonstration, we will be storing credentials using Kubernetes secrets. In a production environment, you would want to use a more fully fledged secret management solution such as Hashicorp Vault, or the external secrets operator and rotate these credentials regularly.

Encode Your API Token:

Before creating the Secret, encode your DigitalOcean API token using base64. You can achieve this in your terminal by running:

echo "<your do api token>"  | base64

⚠️ Replace <your DO API token> with your actual token.

Next, let’s create a provider config:

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
  namespace: crossplane-system
  name: provider-do-secret
type: Opaque
  token: <your b64 encoded DO token>

kind: ProviderConfig
  name: civo-demo
	source: Secret
  	namespace: crossplane-system
  	name: provider-do-secret
  	key: token

In the code above, we create a ProviderConfig resource named civo-demo to reference the Secret containing the credentials. This ProviderConfig essentially tells Crossplane how to connect to your DigitalOcean account.

Provisioning the Database

With the Crossplane provider configured, we're ready to create and provision a PostgreSQL cluster.

1. Apply the DODatabaseCluster Manifest:

To initiate the database provisioning process, apply the following YAML manifest to your Kubernetes cluster using kubectl apply -f <filename>.yaml.

kubectl apply -f - <<EOF
kind: DODatabaseCluster
  name: civo-cluster
	engine: pg # Database engine (PostgreSQL)
	version: "13" # Desired PostgreSQL version
	numNodes: 1 # Number of database nodes in the cluster
	size: db-s-2vcpu-4gb # Resource size for the database cluster
	region: nyc3 # Region where the cluster will be provisioned
	tags: # Optional tags for the cluster
  	- "from-crossplane"
  	- "civo-demo"
	name: civo-demo # Reference to the Crossplane provider config

This manifest defines a DODatabaseCluster resource named civo-cluster. The forProvider section specifies the desired configuration for your database cluster, including:

  • engine: Database engine (set to pg for PostgreSQL)
  • version: PostgreSQL version
  • numNodes: Number of database nodes
  • size: Resource allocation for the cluster
  • region: Region where the cluster will be deployed
  • tags: Optional tags for identification

The providerConfigRef references the civo-demo Crossplane provider config you created earlier, which establishes the connection to your DigitalOcean account.

2. Monitor the Provisioning Process:

Once you apply the manifest, you can track the provisioning status of your database cluster using the kubectl get command:

kubectl get

This command will display information about your civo-cluster resource, including its current status. Initially, you'll likely see a status of Creating or Pending. As Crossplane interacts with the DigitalOcean API, the status will eventually transition to Ready once the database cluster is successfully provisioned.

You can also verify that the cluster was created by heading to the databases tab in your Digital Ocean Dashboard.

Taking a closer look, you can see some of the tags we included in the manifest also show up on the dashboard.

Clean up

Once you're finished experimenting with your database cluster, it's good practice to clean up the resources to avoid unnecessary costs and clutter in your environment. Here's how to remove the database cluster we provisioned:

1. Delete the DODatabaseCluster Manifest:

Use kubectl delete to remove the DODatabaseCluster manifest that created the cluster. This will initiate the deletion process within Crossplane, which will then signal DigitalOcean to delete the actual database cluster.

kubectl delete civo-cluster

2. (Optional) Delete the Crossplane Provider Config (civo-demo):

If you don't plan on using Crossplane with DigitalOcean anymore in this cluster, you can also delete the ProviderConfig resource named civo-demo. This will remove the configuration that Crossplane used to connect to your DigitalOcean account.

kubectl delete providerconfigs civo-demo

Remember, deleting the DODatabaseCluster manifest is sufficient to clean up the database cluster on DigitalOcean. The second step is optional and only needed if you want to completely remove Crossplane's connection to your DigitalOcean account within this cluster.

3. Tearing Down the Civo Kubernetes Cluster (Optional)

While we focused on managing DigitalOcean resources through Crossplane within the Kubernetes cluster, you might also want to remove the Civo Kubernetes cluster itself. To do this, you can use the Civo CLI:

civo k3s delete do-crossplane

Once you're done with the tutorial, it's essential to clean up the resources you created. This section will guide readers on how to remove the database and any associated resources provisioned by Crossplane.


In this tutorial, we explored how to leverage Crossplane within a Kubernetes cluster to simplify the deployment and management of database resources on DigitalOcean.

Remember, avoiding manually deleting resources provisioned with Crossplane is generally recommended. Crossplane is designed to continuously reconcile the desired state defined in your manifests with the actual state of your infrastructure. Manually deleting resources on the cloud provider side can disrupt this process and lead to inconsistencies.


Jubril Oyetunji Twitter

Jubril is a software engineer, primarily focused on building infrastructure using cloud native technologies. When he’s not coding or ranting, he’s sharing his learnings through technical writing.