We’ll deploy a demo application into the Kubernetes cluster using ArgoCD to simulate load on the PostgreSQL cluster.
This is a series of articles, in previous parts we:
- Part 1 - Prepared the environment and installed ArgoCD and GitHub repository.
 - Part 2 - Installed Percona Operator for Postgres and created a Postgres cluster.
 
The application is a custom Go-based service that generates traffic for PostgreSQL, MongoDB, or MySQL.
It uses a dataset of GitHub repositories and pull requests, and mimics real-world operations like fetching, creating, updating, and deleting records.
Load intensity is configurable through a browser-based control panel.
We’ll install it using Helm, tracked and deployed via ArgoCD.
Reference repository: github-stat
Create the ArgoCD Application Manifest
Create a file named argocd-demo-app.yaml in the apps/ directory.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: demo-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/dbazhenov/github-stat
    targetRevision: main
    path: k8s/helm
  destination:
    server: https://kubernetes.default.svc
    namespace: demo-app
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
This will install the Helm chart fromhttps://github.com/dbazhenov/github-stat/tree/main/k8s/helm
By default, the service is configured as LoadBalancer, making it accessible from the internet.
To switch to NodePort (if needed), override the Helm value:
source:
  helm:
    parameters:
      - name: controlPanelService.type
        value: NodePort
We’ll keep default settings in this example.
Push the Application Manifest to GitHub
Track and commit your changes:
git status
git add .
git commit -m "Installing Demo Application in ArgoCD by HELM"
git push origin main 
Expected Git output:
➜  percona-argocd-pg-coroot git:(main) git status
On branch main
Your branch is up to date with 'origin/main'.
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    apps/argocd-demo-app.yaml
nothing added to commit but untracked files present (use "git add" to track)
➜  percona-argocd-pg-coroot git:(main) ✗ git add .
➜  percona-argocd-pg-coroot git:(main) ✗ git commit -m "Installing Demo Application in ArgoCD by HELM"
[main 03ce175] Installing Demo Application in ArgoCD by HELM
 1 file changed, 20 insertions(+)
 create mode 100644 apps/argocd-demo-app.yaml
➜  percona-argocd-pg-coroot git:(main) git push origin main
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 10 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 686 bytes | 686.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:dbazhenov/percona-argocd-pg-coroot.git
   6b2dc98..03ce175  main -> main
➜  percona-argocd-pg-coroot git:(main)
Apply the ArgoCD Application
Deploy the app via:
kubectl apply -f apps/argocd-demo-app.yaml -n argocd
ArgoCD will install the app and has started tracking the app’s HELM chart
Validate the Deployment
Confirm the app status in ArgoCD UI:

Check running pods:
kubectl get pods -n demo-app
Expected pods:
➜  percona-argocd-pg-coroot git:(main) kubectl get pods -n demo-app
NAME                               READY   STATUS    RESTARTS   AGE
demo-app-dataset-6d886f67-j648w    1/1     Running   0          2m52s
demo-app-load-577cff97c9-d8j99     1/1     Running   0          2m52s
demo-app-valkey-74989c9bf7-gjp4x   1/1     Running   0          2m52s
demo-app-web-5b98d4c65c-xmkq9      1/1     Running   0          2m52s
- demo-app-dataset - loads dataset
 - demo-app-load - generates traffic
 - demo-app-valkey - Redis-compatible DB backend
 - demo-app-web - UI dashboard
 
Open the Application Dashboard
Retrieve the external IP:
kubectl get svc -n demo-app
Find the EXTERNAL-IP of demo-app-web-service.
Sample output:
➜  percona-argocd-pg-coroot git:(main) kubectl get svc -n demo-app
NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
demo-app-valkey-service   ClusterIP      34.118.235.203   <none>          6379/TCP       4m59s
demo-app-web-service      LoadBalancer   34.118.232.144   34.28.221.107   80:31308/TCP   4m59s
Access the app in your browser:

Navigate to the Settings tab to configure a PostgreSQL connection.
PostgreSQL Credentials Setup
Percona Operator has already (Application and system users):
- Created schema and database 
cluster1 - Created user 
cluster1 - Stored credentials in 
cluster1-pguser-cluster1secret 
Extract the password:
kubectl get secret cluster1-pguser-cluster1 -n postgres-operator --template='{{.data.password | base64decode}}{{"\n"}}'
Let’s connect to the database from the Demo application using the given user and cluster1-pgbouncer.postgres-operator.svc host
In the Connection String field enter
user=cluster1 password='[PASSWORD]' dbname=cluster1 host=cluster1-pgbouncer.postgres-operator.svc port=5432

The connection has been successfully created, this is good.
To start generating the load, we need to import the Dataset using the Import Dataset button.
Dataset Import Error: Create Schema Denied
During import, the app tries to create a schema.
By default, pgBouncer limits user privileges, preventing this action.
Percona documentation suggests enabling proxy.pgBouncer.exposeSuperusers and creating a privileged user.
We’ll handle this via GitOps. It seems cool that we’ll be doing this with tracking in Git, as these are important settings and we shouldn’t forget about them and turn them off in the future.
Define a New PostgreSQL User
We will make changes to postgres/cr.yaml that will add a new user and also enable the proxy.pgBouncer.exposeSuperusers option.
In the postgres/cr.yaml file I found the users section, uncommented and added my user data.
In postgres/cr.yaml, add:
  users:
    - name: daniil
      databases:
        - demo
      options: "SUPERUSER"
      password:
        type: ASCII
      secretName: "daniil-credentials"
Note: In production, use scoped permissions like "LOGIN CREATE CREATEDB" rather than SUPERUSER.
I also found the proxy.pgBouncer.exposeSuperusers setting and set it to true
Update pgBouncer config:
  proxy:
    pgBouncer:
      replicas: 3
      image: docker.io/percona/percona-pgbouncer:1.24.1
      exposeSuperusers: true
Commit and push:
git status
git add .
git commit -m "Postgres cluster: Creating a new user and pgBouncer.exposeSuperusers"
git push origin main
After a couple of minutes, ArgoCD will synchronize the changes and Percona Operator will create the user and change the configuration.

Connect With the New User
Get the password:
kubectl get secret daniil-credentials -n postgres-operator --template='{{.data.password | base64decode}}{{"\n"}}'
Let’s replace Connection String in Demo application, I got the following string
user=daniil password='iKj:e[wT3*g]OF5+f' dbname=dataset host=cluster1-pgbouncer.postgres-operator.svc port=5432

Click the “Import Dataset” button and wait a few minutes until the import is in Done status in the Dataset tab.

Enable Load Generation
Activate the load generator:
- Toggle Enable Load in the connection settings
 - Click Update Connection
 

Open the Load Generator Control Panel and adjust sliders and toggles as needed:

Conclusion
In this part, we:
- Deployed a demo application via Helm in ArgoCD
 - Connected it to our PostgreSQL cluster
 - Managed PostgreSQL users and access via GitHub and GitOps
 - Imported a dataset and activated the traffic generator through the web UI
 
In Part 4, we’ll deploy Coroot for observability and profiling.
It’s an impressive tool for diagnosing behavior across services in the Kubernetes cluster. ∎




Discussion
We invite you to our forum for discussion. You are welcome to use the widget below.