DevSecOps integrates security into every stage of the software delivery lifecycle rather than treating it as a final gate before release. On RHEL 8 you can assemble a complete DevSecOps pipeline from open-source components, all running on your own infrastructure: a Git server for source control, a CI/CD engine for automation, static analysis and container scanning for early vulnerability detection, a secrets manager for credential hygiene, a private container registry, GitOps-based deployment, and a unified observability stack for production visibility. This guide wires all of those layers together into a single pipeline that covers the full path from developer commit to monitored deployment.

Prerequisites

  • RHEL 8 server (minimum 8 vCPU / 16 GB RAM) with root or sudo access
  • Podman and podman-compose installed (dnf install -y podman podman-compose)
  • A Kubernetes cluster (k3s or full RKE2) reachable from this host for ArgoCD deployment
  • DNS or /etc/hosts entries for each service hostname
  • Ports 3000, 8080, 9000, 8200, 443, 9090, 3100 available
  • Familiarity with Git, Docker/Podman, and basic Kubernetes concepts

Step 1 — Git Server: Deploy Gitea

Gitea is a lightweight self-hosted Git service. Run it with Podman so it starts automatically with the system.

mkdir -p /opt/devsecops/gitea/data

podman run -d 
  --name gitea 
  --restart always 
  -p 3000:3000 
  -p 2222:22 
  -v /opt/devsecops/gitea/data:/data:z 
  -e GITEA__database__DB_TYPE=sqlite3 
  docker.io/gitea/gitea:latest

# Generate a systemd unit so Gitea survives reboots
podman generate systemd --name gitea --files --new
mv container-gitea.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable container-gitea.service

Open http://<SERVER_IP>:3000 in a browser to complete the initial setup wizard. Create an admin account and a test repository named app — this repository will be the trigger point for the entire pipeline.

Step 2 — CI/CD Engine: Deploy Jenkins

Jenkins is used here for its broad plugin ecosystem. Deploy it as a container alongside a Docker-in-Docker (DinD) sidecar so pipeline stages can build container images.

mkdir -p /opt/devsecops/jenkins

podman run -d 
  --name jenkins 
  --restart always 
  -p 8080:8080 
  -p 50000:50000 
  -v /opt/devsecops/jenkins:/var/jenkins_home:z 
  -v /run/user/0/podman/podman.sock:/var/run/docker.sock:z 
  docker.io/jenkins/jenkins:lts-jdk17

Retrieve the initial admin password and complete the setup wizard:

podman exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

Install the recommended plugins plus Gitea, Pipeline, and Credentials Binding. Configure a Gitea webhook (Settings → Webhooks → Add Webhook → Gitea) pointing to http://<SERVER_IP>:8080/gitea-webhook/post so every push triggers a Jenkins build.

Step 3 — Code Scanning: SonarQube and Trivy

SonarQube performs Static Application Security Testing (SAST) on source code; Trivy scans built container images for CVEs.

# SonarQube (requires at least 4 GB RAM dedicated)
mkdir -p /opt/devsecops/sonar/{data,logs,extensions}
sysctl -w vm.max_map_count=524288
sysctl -w fs.file-max=131072

podman run -d 
  --name sonarqube 
  --restart always 
  -p 9000:9000 
  -e SONAR_ES_BOOTSTRAP_CHECKS_DISABLE=true 
  -v /opt/devsecops/sonar/data:/opt/sonarqube/data:z 
  -v /opt/devsecops/sonar/logs:/opt/sonarqube/logs:z 
  docker.io/sonarqube:community

# Trivy — install from RHEL 8 / EPEL-compatible repo
dnf install -y epel-release
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.51.0

# Test Trivy on an image
trivy image --exit-code 1 --severity HIGH,CRITICAL alpine:latest

Step 4 — Secrets Management: HashiCorp Vault

Vault stores all pipeline credentials (registry passwords, SSH keys, API tokens) and injects them into Jenkins as short-lived secrets so plaintext credentials never appear in Jenkinsfiles or environment variables.

mkdir -p /opt/devsecops/vault/{config,data,logs}

cat > /opt/devsecops/vault/config/vault.hcl << 'EOF'
ui = true
storage "file" { path = "/vault/data" }
listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_disable = true          # Use TLS termination at a reverse proxy in production
}
EOF

podman run -d 
  --name vault 
  --restart always 
  -p 8200:8200 
  --cap-add IPC_LOCK 
  -v /opt/devsecops/vault/config:/vault/config:z 
  -v /opt/devsecops/vault/data:/vault/data:z 
  docker.io/hashicorp/vault:latest 
  vault server -config=/vault/config/vault.hcl

# Initialize and unseal (save the unseal keys securely)
export VAULT_ADDR=http://127.0.0.1:8200
podman exec -e VAULT_ADDR=$VAULT_ADDR vault vault operator init -key-shares=1 -key-threshold=1
# podman exec vault vault operator unseal 

Step 5 — Container Registry and GitOps Deployment

Harbor provides a private, security-scanning-enabled OCI registry. ArgoCD implements GitOps — it watches a Git repository for Kubernetes manifests and reconciles the cluster to match.

# Harbor — install via Helm on your Kubernetes cluster
helm repo add harbor https://helm.goharbor.io
helm install harbor harbor/harbor 
  --namespace harbor --create-namespace 
  --set expose.type=nodePort 
  --set expose.tls.enabled=false 
  --set externalURL=http://192.168.1.10:30080

# ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd 
  -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# Get the initial ArgoCD admin password
kubectl get secret argocd-initial-admin-secret 
  -n argocd -o jsonpath='{.data.password}' | base64 -d

Create an ArgoCD Application that points to the k8s/ directory in your Gitea repository. ArgoCD will automatically apply changes whenever a commit updates the Kubernetes manifests — no manual kubectl apply required.

Step 6 — Observability: Prometheus, Grafana, Loki, and Alertmanager

Deploy the kube-prometheus-stack (includes Prometheus, Grafana, and Alertmanager) and Loki for log aggregation using Helm.

# kube-prometheus-stack
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install monitoring prometheus-community/kube-prometheus-stack 
  --namespace monitoring --create-namespace 
  --set grafana.adminPassword='SecureGrafana1!' 
  --set alertmanager.enabled=true

# Loki stack (Loki + Promtail log shipper)
helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki-stack 
  --namespace monitoring 
  --set loki.enabled=true 
  --set promtail.enabled=true

# Access Grafana
kubectl port-forward -n monitoring svc/monitoring-grafana 3001:80 &
# Open http://localhost:3001 — user: admin, password: SecureGrafana1!

In Grafana, add the Loki data source (http://loki:3100) and import the pre-built Kubernetes / Compute Resources / Cluster dashboard (ID 15760) alongside the Loki Logs dashboard to get immediate infrastructure and application log visibility.

The complete pipeline flow is: developer commits to Gitea → Gitea webhook triggers Jenkins → Jenkins runs SonarQube SAST → Jenkins builds the container image → Trivy scans the image for CVEs → on pass, Jenkins pushes the image to Harbor → Jenkins updates the Kubernetes manifest tag in Git → ArgoCD detects the change and rolls out the new deployment → Prometheus scrapes metrics, Loki aggregates logs, and Alertmanager fires notifications on anomalies.

Conclusion

You now have a production-grade DevSecOps pipeline running entirely on RHEL 8, covering every critical discipline: source control, automated CI/CD, SAST, container vulnerability scanning, secrets management, private image hosting, GitOps deployment, and full-stack observability. Each component is containerized and independently upgradeable, and the pipeline enforces security gates automatically so vulnerabilities cannot silently reach production. The modular design means you can swap any component (for example, replacing Jenkins with Tekton or Drone) without disrupting the rest of the stack.

Next steps: How to Configure Policy-as-Code with OPA and Gatekeeper on Kubernetes, How to Set Up Automated DAST Scanning with OWASP ZAP in Jenkins, and How to Manage Kubernetes Secrets with HashiCorp Vault Injector.