Alexander's Portfolio

A collection of my projects and thoughts!

24 November 2025

GitOps with ArgoCD and Helm charts

by Alexander

Using GitOps in my Kubernetes cluster

In order to learn about Kubernetes I recently set up my own cluster from scratch, as documented in the previous post Kubernetes the hard way.

I used the cluster to deploy my own Limit order book application. I started out by putting the manifest files in in a /k8s subdirectory in the application Github repository.

My workflow was as follows.

  1. Update the application
  2. Create a tag in git and push it to GitHub
  3. Pushing the tag to GitHub would trigger GitHub actions to build a docker image.
  4. I would then manually run kubectl set image deployments/limit-order-book limit-order-book=limit-order-book:<NEW-TAG>

I wanted to automate this flow and make it work closer to how it would in a production environment.

Templating with Helm charts

I started by converting my manifests into Helm charts.

By creating templates for all my resources that all referenced a single values-file made it got easier to get an overview of the current state of my cluster. It also made it easier to switch between different versions of the application.

Automated deployments with ArgoCD

After installing ArgoCD in my cluster I could add my application running the following command.

argocd app create limit-order-book --repo https://github.com/Alexandoooor/limit-order-book-helm.git --path limit-order-book --dest-server https://kubernetes.default.svc --dest-namespace default

By setting the sync-policy to automated ArgoCD will automatically sync the application when it detects differences in the manifests in git.

argocd app set limit-order-book --sync-policy automated

Secret management

There was one thing missing to get the automated deployments working, secrets. I needed both imagePullSecrets for getting the docker image from a private repository, as well as the password for the Postgres database.

I had previously manually added the secrets after setting up the cluster. But this approach is the opposite of automation. I instead decided to use sealed secrets.

Sealed secrets works by installing a controller in the cluster. The controller has a public encryption key, which you use to encrypt a secret, the Postgres password for example. The result is a sealed (encrypted) secret which can be included in the Helm charts of the application. The sealed secrets controller is the only holder of the private key needed unencrypt (unseal) the secret so that it can be used as a regular Kubernetes secret.

kubectl create secret generic limit-order-book-postgres-secret \
  --from-literal=ps_password=hunter2 \
  --dry-run=client -o yaml > secret.yaml

The above command outputs the secret.yaml with the content below.

apiVersion: v1
kind: Secret
metadata:
  name: limit-order-book-postgres-secret
type: Opaque
stringData:
  ps_password: hunter2

Running kubeseal as follows uses the public key to encrypt the secret into sealedsecret.yaml:

kubeseal --format yaml < secret.yaml > sealedsecret.yaml

The resulting sealedsecrets.yaml contains the encrypted secret which can safely be added to the git repository, since the controller is the only one holding the private key needed to unseal the secret.

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: limit-order-book-postgres-secret
  namespace: default
spec:
  encryptedData:
    ps_password: AgByvKX3a...
tags: kubernetes - cloud