Hardening your infrastructure as code pipeline with self-hosted OpenTaco (formerly Digger)
Deploy self-hosted OpenTaco (formerly Digger) on Civo Kubernetes with the full umbrella Helm chart — orchestrator, state backend, drift detection, TLS via cert-manager, and GitHub App integration — to harden your IaC pipeline for team-scale production use.
Written by
Technical Writer at Civo
Written by
Technical Writer at Civo
In part 1 of our tutorial series, we set up a simple Digger pipeline to perform infrastructure as code (IaC) deployments, all within a GitHub pipeline. But what if we wanted to expand such a deployment for a larger installation or want it hardened for production deployments? We will look at deploying the orchestrator backend.
Utilizing the orchestrator backend will allow for efficient management of CI pipelines, especially for teams working on multiple PRs simultaneously. For this reason, this tutorial will dive into configuring and deploying the Digger orchestrator backend, setting up secure webhooks using cert-manager, and integrating it with GitHub to manage larger infrastructure environments efficiently.
A note on the rebrand: Digger is now OpenTaco
If you followed part 1, the self-hosting story has changed since then, so it is worth a short orientation before we start.
Digger has been rebranded to OpenTaco. The single digger-backend service you may have deployed before is now called the orchestrator, and it is one of several services that make up the platform. The old standalone chart still exists in the docs, but it is filed under deprecated installs. The current, supported way to self-host is an umbrella Helm chart that deploys the whole platform together.
The commands and architecture in this guide reflect OpenTaco as it stands today. Where you see the name Digger in a service image or an environment variable, that is the orchestrator's heritage showing through.
What is the orchestrator backend?
The orchestrator backend is a service that triggers Pipeline Runs. Events within the source code management system usually trigger most CI pipeline runs, code commits, pull requests, and other internal activity. Most CI systems also provide a way for CI activities to be triggered externally. This is what the orchestrator uses to help build a configuration that will work more efficiently with multiple users and larger environments.
Some of the other core points include:
- Quicker response to PR comments and faster status check updates
- Parallelization where appropriate
- Appropriate queuing with multiple PRs
- PR level locks
In OpenTaco the orchestrator is no longer the whole story. It is the piece that talks to GitHub, but it now sits alongside services for state management, drift detection, token issuance, and remote runs.
The OpenTaco architecture
Before we install anything, here is what the umbrella chart actually deploys, so the moving parts are not a surprise.
- UI: The public entrypoint. It serves the dashboard and proxies the backend APIs.
- Orchestrator: Processes GitHub webhooks and creates Terraform or OpenTofu workflow runs. This is the former digger-backend.
- Drift: Runs drift detection and sends notifications.
- Statesman: A Terraform Cloud compatible state backend, backed by S3-compatible object storage.
- Token service: Issues API tokens for Statesman authentication.
- Sidecar: Manages remote run sandboxes. Optional, and we leave it off in this guide.
Three of these services need a database, and Statesman needs object storage. The platform reference chart below provides both, so you have a working baseline to build on.
Two third-party dependencies are worth knowing up front. OpenTaco uses WorkOS for authentication, so you will need a WorkOS account to log in. It also uses GitHub for PR automation, which we wire up near the end.
Prerequisites
- A verified Civo account
- The Civo CLI installed and authenticated
- kubectl and Helm installed
- A WorkOS account for authentication
- A GitHub organization or account where the app will be installed
Creating a Civo Kubernetes cluster
We will provision a small cluster from the command line. Two medium nodes are enough for the whole platform:
civo kubernetes create taco-demo \--size g4s.kube.medium \--nodes 2 \--create-firewall \--wait --save --switch
The --save --switch flags merge the new kubeconfig and make it your active context. Confirm the nodes are ready:
kubectl get nodes
Every Civo cluster gets a DNS name of the form <cluster-id>.k8s.civo.com. We use it as the public URL for OpenTaco, so record it now:
civo kubernetes show taco-demo# Note the "DNS A record" value, then:export HOST=<your-cluster-id>.k8s.civo.com
Installing the platform baseline
OpenTaco ships an optional platform reference chart that provisions Traefik for ingress, MinIO for object storage, and a CloudNativePG PostgreSQL cluster with the three databases the services need. Treat it as a reference baseline, not a production-hardening blueprint. For a real deployment you would bring your own managed database and object storage, and on Civo you could point Statesman at Civo object storage instead of MinIO.
Create the namespaces:
kubectl create namespace opentacokubectl create namespace traefik
Install the platform reference chart in two steps. The chart bundles the CloudNativePG operator and a PostgreSQL cluster resource in one release, so a single install fails on CRD ordering. Install with the database disabled first, let the operator come up, then enable it:
cat > platform-values.yaml <<'EOF'cnpg:nameOverride: cloudnative-pgpostgresql:enabled: falseEOFhelm upgrade --install opentaco-platform-reference \oci://ghcr.io/diggerhq/helm-charts/opentaco-platform-reference \--version 0.1.0 -n opentaco -f platform-values.yamlkubectl rollout status deployment/opentaco-platform-reference-cloudnative-pg \-n opentaco --timeout=180ssed -i '' 's/enabled: false/enabled: true/' platform-values.yamlhelm upgrade opentaco-platform-reference \oci://ghcr.io/diggerhq/helm-charts/opentaco-platform-reference \--version 0.1.0 -n opentaco -f platform-values.yaml
Gotcha: the cnpg.nameOverride: cloudnative-pg value above is not optional. Without it, the bundled CloudNativePG operator cannot find its own deployment during startup and crash-loops. See the tracking note at the end of this guide.
Wait for the PostgreSQL cluster and MinIO to report ready:
kubectl get pods -n opentacokubectl get cluster.postgresql.cnpg.io -n opentaco
Setting up cert-manager
As we will expose the UI on a public address over HTTPS, we need a signed TLS certificate. To configure one, leverage the cert-manager Civo Marketplace app. The core cert-manager is installed, but we still need to configure it:
civo kubernetes applications add cert-manager --cluster taco-demo
The following will set up a cluster-issuer to allow us to continue:
kubectl apply -f - <<EOFapiVersion: cert-manager.io/v1kind: ClusterIssuermetadata:name: letsencrypt-prodspec:acme:email: your-email@example.compreferredChain: ""privateKeySecretRef:name: prod-letsencrypt-keyserver: https://acme-v02.api.letsencrypt.org/directorysolvers:- selector: {}http01:ingress:class: traefikEOF
Configuring WorkOS authentication
WorkOS must be configured before anyone can sign in. In the WorkOS dashboard, enable AuthKit, then set the following against your application, substituting your cluster host:
- Redirect URI:
https://${HOST}/api/auth/callback - Sign-out redirect:
https://${HOST}/logout - CORS allowed origin:
https://${HOST} - A webhook for the
user.createdevent pointing athttps://${HOST}/api/auth/workos/webhooks
Then collect three values you will need for the secrets below: the Client ID, an API key, and the webhook signing secret.
Creating the application secrets
OpenTaco reads its configuration from Kubernetes Secrets. Rather than putting values inline in the chart, we pre-create the secrets and reference them, which keeps credentials out of your values file and works cleanly with tools like the External Secrets Operator later.
Generate the shared internal secrets once and create the UI, orchestrator, statesman, and drift secrets. The stringData fields below take plain text and the API server encodes them:
INTERNAL_SECRET=$(openssl rand -base64 36)ENCRYPTION_SECRET=$(openssl rand -base64 24)STATE_KEY=$(openssl rand -base64 36)kubectl apply -f - <<EOFapiVersion: v1kind: Secretmetadata: { name: ui-secrets, namespace: opentaco }type: OpaquestringData:WORKOS_CLIENT_ID: "<your-workos-client-id>"WORKOS_API_KEY: "<your-workos-api-key>"WORKOS_WEBHOOK_SECRET: "<your-workos-webhook-secret>"WORKOS_COOKIE_PASSWORD: "$(openssl rand -base64 24)"WORKOS_REDIRECT_URI: https://${HOST}/api/auth/callbackPUBLIC_URL: https://${HOST}ALLOWED_HOSTS: ${HOST},localhostORCHESTRATOR_BACKEND_URL: http://opentaco-taco-orchestrator-web:3000ORCHESTRATOR_BACKEND_SECRET: ${INTERNAL_SECRET}STATESMAN_BACKEND_URL: http://opentaco-taco-statesman:8080TOKENS_SERVICE_BACKEND_URL: http://opentaco-taco-token-service:8081DRIFT_REPORTING_BACKEND_URL: http://opentaco-taco-drift:3004EOF
NOTE: Set these values without surrounding quotes. If you build the secret from one of the shipped .env example files with kubectl create secret --from-env-file, the quotes in those files are kept literally, and the UI fails to start with Failed to parse URL.
Create three more secrets the same way, named taco-orchestrator-secrets, statesman-secrets, and drift-secrets, wiring each service to its database in the shared PostgreSQL cluster. The full set of variables is listed in the OpenTaco configuration docs. One consistency rule matters most: the orchestrator's DIGGER_INTERNAL_SECRET must equal the ORCHESTRATOR_BACKEND_SECRET we just set in ui-secrets, which is why we generated INTERNAL_SECRET once and reuse it.
Installing OpenTaco
With the baseline and secrets in place, install the umbrella chart. We start from the platform-reference values, which pre-wire every service to the in-cluster PostgreSQL and MinIO, then layer our own overrides on top. Helm deep-merges values files in the order they are passed, so the two files stay cleanly separated.
Fetch the platform-reference values from the OpenTaco repo:
curl -sO https://raw.githubusercontent.com/diggerhq/digger/develop/self-hosting/kubernetes/helm-charts/opentaco/values.platform-reference.yaml
Then create values-opentaco.yaml, which points each service at its pre-created secret, sets the public URL, and enables the UI ingress:
taco-orchestrator:digger:secret:useExistingSecret: trueexistingSecretName: taco-orchestrator-secretstaco-statesman:taco:publicBaseUrl: https://<your-cluster-id>.k8s.civo.comsecretKey: <generate with: openssl rand -base64 36>secret:useExistingSecret: trueexistingSecretName: statesman-secretstaco-drift:drift:secret:useExistingSecret: trueexistingSecretName: drift-secretstaco-sidecar:enabled: falsetaco-ui:ui:secret:useExistingSecret: trueexistingSecretName: ui-secretsingress:enabled: trueclassName: traefikannotations:cert-manager.io/cluster-issuer: letsencrypt-prodhosts:- host: <your-cluster-id>.k8s.civo.compaths:- path: /pathType: Prefixtls:- secretName: opentaco-ui-tlshosts:- <your-cluster-id>.k8s.civo.com
Install:
helm upgrade --install opentaco \oci://ghcr.io/diggerhq/helm-charts/opentaco \--version 0.1.2-public \-f values.platform-reference.yaml \-f values-opentaco.yaml \-n opentaco
Watch the pods settle:
kubectl get pods -n opentaco
NOTE: At the time of writing, the Statesman and Token service charts do not pull every database and storage variable into the pod environment, so those two services may need the OPENTACO_QUERY_BACKEND, OPENTACO_POSTGRES_*, and OPENTACO_S3_* values set directly on the deployment before they start cleanly. This is a chart bug, tracked in the note at the end.
Once all pods are running, the cert-manager certificate is issued and the UI is reachable over HTTPS:
curl -sI "https://${HOST}/"
Open https://${HOST} in a browser and you are met with the OpenTaco sign-in screen.
The OpenTaco landing and sign-in page, served over HTTPS on the Civo cluster.
Sign up the first user through the WorkOS-backed login, and you land on the dashboard.
The OpenTaco dashboard after signing in, with the Units, Repos, Projects, and Drift sections in the sidebar.
Configuring GitHub integration
The orchestrator processes GitHub webhooks, so it needs a GitHub App whose events point at your cluster. A self-hosted deployment creates its own app rather than installing the hosted one, so that the app's webhooks reach your orchestrator and its credentials belong to you.
The intended flow is to open the repository setup wizard in the dashboard, click "Connect with GitHub", and complete the GitHub App creation.
The repository setup wizard: connect GitHub, add the workflow, add digger.yml, then Terraform.
You then copy the five values the app produces into your orchestrator and drift secrets:
GITHUB_APP_IDGITHUB_APP_CLIENT_IDGITHUB_APP_CLIENT_SECRETGITHUB_APP_PRIVATE_KEY_BASE64GITHUB_WEBHOOK_SECRET
After saving them, restart the orchestrator and drift services so the new credentials load, then install the app on the repositories you want Digger to manage.
Running Digger in the pipeline
Once the GitHub App is connected, configure Digger in the pipeline to use your self-hosted orchestrator. It is the same as in part 1, with one parameter added for the orchestrator URL:
- name: digger runuses: diggerhq/digger@latestwith:digger-hostname: https://<your-cluster-id>.k8s.civo.com
Cleaning up
A running cluster costs money, so remove it when you are done:
civo kubernetes remove taco-demo
If this was a throwaway run, also tidy the external accounts: delete the GitHub App from your organization settings and remove the WorkOS application or environment you configured.
Summary
You have now self-hosted OpenTaco, the platform Digger became. We deployed the orchestrator and its companion services with the umbrella chart, gave them a database and object storage with the platform reference chart, secured the UI with cert-manager and a Let's Encrypt certificate, and wired authentication through WorkOS.
Now that the platform is running, you can explore options for scaling and hardening this setup:
- The database and object storage here run inside Kubernetes as a reference baseline. For critical environments, move PostgreSQL and state storage onto managed, backed-up infrastructure. On Civo, Statesman can use Civo object storage for state.
- Keep all secrets in a secret manager rather than inline values, and treat the shared internal secrets as a single rotation unit.

Technical Writer at Civo
Jubril Oyetunji is a DevOps engineer and technical writer with a strong focus on cloud-native technologies and open-source tools. His work centers on creating practical tutorials that help developers better understand platforms such as Kubernetes, NGINX, Rust, and Go.
As a contract technical writer, Jubril authored an extensive library of technical guides covering cloud-native infrastructure and modern development workflows. Many of his tutorials achieved strong search rankings, helping developers around the world learn and adopt emerging technologies.
Share this article
Further Reading
9 October 2024
Automating infrastructure as code: Deploying Kubernetes with Digger and GitHub actions
19 September 2024
Self hosting GitHub Actions runner on Civo
15 January 2024