architecture
The site is the portfolio.
This website doesn't just talk about DevSecOps — it runs on a stack that demonstrates it. Every component is provisioned with IaC, every deployment is GitOps-driven, every secret is encrypted at rest. Here's the full breakdown.
request flow
ci/cd pipeline
Push / PR
Developer pushes code to GitHub
Lint + Scan
ESLint, Prettier, Trivy, Checkov
Build Image
Multi-stage Docker build
Push to ghcr.io
Tagged :sha-<SHA> + :latest
Bump SHA
CI commits new tag to deployment.yaml
ArgoCD Sync
Cluster self-heals within ~3min
Live
xavifortes.com serves new build
full stack breakdown
Edge & Security
Cloudflare WAF
IaC — TerraformDDoS protection, bot management, custom WAF rules, rate limiting. SSL/TLS Full Strict mode. Acts as the global entry point for all traffic to xavifortes.com.
Cloudflare DNS
IaC — TerraformDNS records managed 100% in Terraform via the cloudflare/cloudflare provider. No manual clicks in the dashboard. A record targets GRA8 HAProxy.
Load Balancer / Bastion
OVH GRA8 — HAProxy
OVH GRA8 · 2vCPU / 2GBOVH VPS in Gravelines (GRA8). HAProxy routes :80/:443 to MAD1 Traefik, :6443 to kubeadm API server, :25565 to Minecraft. Provisioned with OpenTofu.
Kubernetes Cluster
k3s HA — OVH MAD1 (3 nodes)
k3s v1.33.5 · OVH MAD13-node etcd k3s cluster on OVH Madrid LocalZone. Each node is 4vCPU/8GB. OpenTofu provisions the VPS instances; Ansible bootstraps k3s. Kubeconfig fetched via SOPS-encrypted vars.
Traefik Ingress
Helm · ArgoCD-managedHandles TLS termination inside the cluster. Receives traffic from GRA8 HAProxy and routes to the correct Kubernetes service. IngressRoute CRDs used for routing.
cert-manager
Helm · ArgoCD-managedAutomatic TLS certificate provisioning from Let's Encrypt via ACME HTTP-01. Certificate resources live in the k8s manifests in this repo.
Longhorn
Helm · ArgoCD-managedDistributed block storage across the 3 MAD1 nodes. Used for any stateful workloads. Portfolio site is stateless — Longhorn used by Matrix, Grafana, InfluxDB, etc.
GitOps — ArgoCD
ArgoCD
App-of-Apps patternInstalled in the MAD1 cluster. Watches this repository's kubernetes/apps/ directory. Any merged change to the image tag or manifests is automatically applied to the cluster within ~3 minutes.
Image Tag Auto-Update
GitHub Actions → git commitThe CI pipeline commits the new ghcr.io image SHA back to deployment.yaml on every merge to main. ArgoCD detects the drift and self-heals. No manual kubectl apply, ever.
CI/CD — GitHub Actions
Lint & Format
Pull Request gateESLint + Prettier on Astro/TypeScript. tofu fmt + tofu validate on the Cloudflare Terraform. Runs on every PR.
Security Scans
Trivy · CheckovTrivy scans the built Docker image for CVEs (CRITICAL/HIGH = fail). Checkov scans all Terraform configs for misconfigurations. Both must pass before merge.
Docker Build & Push
ghcr.io · multi-stageMulti-stage Dockerfile: node:20-alpine builds Astro, nginx:alpine serves the static dist/. Image pushed to ghcr.io/xavifortes/web with both :sha-<SHA> and :latest tags.
Tag Bump Commit
GitOps triggerAfter a successful push, the workflow updates kubernetes/apps/xavifortes-web/deployment.yaml with the new image SHA and commits it back to main, triggering ArgoCD.
Container Registry
GitHub Container Registry (ghcr.io)
ghcr.io/xavifortes/webPrivate registry. Images tagged by git SHA for full traceability. ArgoCD pulls from ghcr.io using a k8s imagePullSecret. No self-hosted registry ops overhead.
Homelab (Proxmox)
almond — Ryzen 7 5800X / 64GB
Proxmox 8.4Primary Proxmox node. Hosts kubeadm k8s cluster (3 VMs), k3s-ha node (1 VM), and LXCs: cloudflared, WireGuard, PostgreSQL, Minecraft, coder-server.
peanut — Ryzen 5 3600 / 32GB
Proxmox 8.4Secondary Proxmox node. Hosts: TrueNAS SCALE (ZFS NAS), Home Assistant OS, nginx proxy manager, AdGuard, Homarr, PostgreSQL replica.
Self-Hosted GitHub Runners
ARC 0.9.3ARC (Actions Runner Controller) on the MAD1 k3s cluster provides ephemeral self-hosted runners for the shellnet-infrastructure repo CI. Terraform plans run inside the cluster with LAN access.
source code
Both repositories are public. Read the code, not just the claims.