> ## Content Index
> Fetch the complete content index at: https://portfolio.cedric-bauer.ch/llms.txt
> Use this file to discover other available public pages before exploring further.

# Automated App creation using ArgoCD and Kubernetes
- URL: https://portfolio.cedric-bauer.ch/fully-automated-kubernetes-deployment/
- Published: 2026-01-07T12:34:49.000Z
- Updated: 2026-10-01T04:11:43.000Z
- Author: Cedric Bauer
- Tags: #Import 2026-09-09 18:56, Kubernetes, DevOps

The goal of this project was to build a automated, secure, GitOps-driven platform where new customer applications (e.g. WordPress) can be deployed simply by adding a folder to a Git repository.

In this project I configured a one node K8s setup which included ArgoCD, Backup via Velero and monitoring with Grafana and Loki. The setup used ArgoCD Appsets to automatically create setups which were present in a configured git repository. 

---

## Overview of the setup

As I wanted the setup to be publicly available I rented a VM at infomaniak. The reason why this setup is only a one node setup is simply to safe costs.  
The necessary helm configurations were saved in two Github repositorys. To backup the cluster and to save logs and metrics I used a S3 Bucket, which was also rented at infomaniak. 

![](https://portfolio.cedric-bauer.ch/content/images/2026/09/overview-k8s.png)

For the ingress controller I used the official nginx-ingress Chart from Kubernetes and enabled its mod security component to utilize it as a WAF. 

### Security components

- WireGuard VPN for admin access
- Only open ports in firewall are 80/443/51820
- ModSecurity WAF with CRS + custom rules
- TLS automation via cert-manager
- SealedSecrets for encryption in GitHub
- Separated repository for customer deployments and infrastructure components

---

## K8s Setup

The initial node has 12GB of Ram, 4 vCPU's and runs Debian 12\. To access everything securely and to close all unnecessary ports I setup a wireguard VPN. Afterwards I could open port 443, 80 and 51820 in the firewall and close all other ports. After the VPN connection was established I setup K8s with the network plugin weave. For storage I utilized the 200GB local storage as I only have one node so no need for read-write-many support on the storage and I did not want to create extra costs using azure blob or similar. I also installed the cert-manager helm chart to automate the certificate management via lets encrypt. To secure the data in the GitHub repository I setup the [Bitnami selaedsecret controller](https://github.com/bitnami-labs/sealed-secrets?ref=portfolio.cedric-bauer.ch). This allows me to encrypt secrets and place them into the repository without any risks as they can only be unsealed by the sealed secrets controller which is running in the cluster. 

---

## ArgoCD Setup

First of course I setup the ArgoCD helm chart. In my corresponding git repository I created a folder named "management appsets". The contained yaml file defined a ArgoCD ApplicationSet. ApplicationSets allow me to automatically generate ArgoCD Applications based on folder structure. This removes manual work and ensures every new customer deployment follows the same structure and policies. The file looks like this:

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/carbon-1.png)

I created a directory application in ArgoCD which deploys all yaml files in a directory. This deploys and updates the appset in the cluster. 

This then enables me to create subdirectories in the folder customer-deployments in my repository. After I create a subfolder which contains the necessary files to setup a helm chart, ArgoCD will create an application with following configuration:  

- **Name of the application**: Will be the name of the subdirectory
- **ArgoCD Project**: customer-deployments
- **Helm source files**: values.yaml and values-custom.yaml
- **Source GitHub repository**: [git@github.com](mailto:git@github.com):myuser/myrepo.git
- **Source GitHub repository targetrevision**: main
- **Path inside the repository**: the full path to the subdirectory
- **K8s Cluster to deploy to**: local K8s instance
- **K8s Namespace**: customer-deployments-{the name of the subdirectory}

If I now create a subfolder in the directory customer-deployments with following content:

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/carbon-8-1.png)

ArgoCD will create a helm application named "sample-wordpress" in the namespace "customer-deployments-sample-wordpress" und keep it up to date. 

![](https://portfolio.cedric-bauer.ch/content/images/2026/10/kubernetes-customer-deployment.png)

---

## Nginx and WAF

To manage traffic I used the official nginx-ingress helm chart. 

### WAF

As this setup was mainly intended for Wordpress deployments I wanted a WAF to protect the deployments. For this I used the nginx module modsecurity. This can be enabled in the values.yaml of the helm chart, which as well is maintained in a separate git repository and kept up to date by ArgoCD. 

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/carbon-9.png)

First the module is loaded, then enabled globally, then the full OWASP CSR ruleset is loaded and finally some [custom rules](https://github.com/Rev3rseSecurity/wordpress-modsecurity-ruleset/tree/master?ref=portfolio.cedric-bauer.ch) tailored for Wordpress are loaded. These custom rules are injected via volume mounts from ConfigMaps. To fine-tune the setup the crs-config is also imported via ConfigMap. This initially defines that for every rule matched the request should be blocked with a 403 and following contains all exclusions like this sample:

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/carbon-11.png)

To achieve a higher security most of the exclusions only work for the VPN network so they do not exclude requests from public visitors. 

### TLS

To automate TLS certificates I used the [helm chart cert-manager](https://cert-manager.io/docs/installation/helm/?ref=portfolio.cedric-bauer.ch). This enables me to automatically generate TLS certificates via lets encrypt, which will also be automatically renewed. To utilize this feature in an application I can now just add following TLS snippet to the nginx configuration of the application:

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/carbon-12.png)

And as long as the domain actually points to my K8s instance the TLS certificate is created inside a kubernetes secret named "ghost-tls" in the application namespace. Cert-manager will also take over the renewal process. 

---

## Backup

As a backup solution I used [velero](https://velero.io/?ref=portfolio.cedric-bauer.ch). Velero makes it very easy to backup the cluster and all its components into a S3 Bucket. With a "schedule" object I could define what should be backed up how often:

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/carbon-13.png)

So this schedule creates a daily backup at 02:00 a clock and includes all resources and namespaces. With the velero CLI client backups can also be triggered manually as well as restored. 

---

## Monitoring

For the monitoring following components are used:

- **kube-prometheus-stack**: (Prometheus, Alertmanager and Grafana)
- **Loki**
- **Promtail**

Promtail scrapes the logs of the pods and forwardes them to Loki. Loki saves them in the same S3 storage as backups are done. I chose Loki because it stores logs cheaply in S3 and integrates natively with Grafana, also it is resource efficient (depending on the configuration) and my node was already low on resources then. Prometheus collects the required metrics and logs and metrics can then be displayed in Grafana. To simplify the creation of dashboards I maintain the monitoring stack as well in the GitHub repository which also contains the ingress helm chart. So the monitoring stack is also synced via ArgoCD. The dashboards as well as the alerting rules are maintained in ConfigMap's and volume mounted into the Grafana pod. For alerting I included SMTP credentials. Finally this enables prebuilt dashboards for all applications like following:

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/Screenshot-2026-01-07-at-18.07.31.png)

When a alerting rule is triggered a alert will be sent via email:

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/image-1.png)

---

## Conclusion

So in the end the setup for new Wordpress deployments is fully automated. To create a new instance a folder will be created in the customer-deployments folder inside the specific GitHub repository. Inside the standard structure for a helm deployment should be present, this can also be standardized, as only the values-custom.yaml requires any changes like the domain to be hosted to and the sealed secrets for passwords and so on. The structure for a new app would look like this:

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/carbon-14.png)

Specifically the values-custom.yaml looks like following:

![](https://portfolio.cedric-bauer.ch/content/images/2026/01/carbon-15.png)

The rest of the "always same" configuration lives in values.yaml. 

After the folder is created the appset will pick it up and create an ArgoCD helm application with the name "new-wordpress" in the "customer-deployments-new-wordpress" namespace. As long as the values-custom.yaml contains a valid domain which also points to the K8s instance the TLS certificate will be created automatically by the cert-manager. As Modsecurity is also enabled globally for all deployments it is also protected by the WAF. To setup alerting rules or Grafana dashboards the ConfigMap can be enhanced with the necessary content. Afterwards we will have a dashboard in Grafana for the app as well as functioning alerting via Email. On 02:00 a Clock after the deployment velero will also pickup the new deployment in the new namespace so the new setup will also be backed up. 

So if I now wanted to fully automate the process I only would have to insert the subfolder in the initial GitHub repository and customize some values. Sealing of sealed secrets could also be done in some kind of pipeline or automation as the certificate to seal a secret can be extracted and poses no security risk as it can only be used to seal and not to unseal secrets, so no connection to the actual controller is required. In the same automation could be introduced to add the alerting rules and custom Grafana dashboards for the new application in the other repository. 

The only step having to be done by hand is configuring the domain to point to the K8s instance. Afterwards the deployment will get a valid TLS certificate and be protected by the WAF as well ass being monitored and backed.