Prerequisites#

This guide assumes you have the following prerequisites in place:

Semaphore#

Modern UI for Ansible, Terraform/OpenTofu/Terragrunt, PowerShell and other DevOps tools.

Installation#

  1. Create the following directory structure for Semaphore:

    semaphore/
    ├── semaphore-hr.yml
    ├── semaphore-repo.yml
    ├── semaphore-secret.yml
    ├── semaphore-ingress.yml
    └── semaphore-db.yml
  2. Add the following content to semaphore/semaphore-repo.yml:

    ---
    apiVersion: source.toolkit.fluxcd.io/v1
    kind: HelmRepository
    metadata:
      name: semaphore
      namespace: flux-system
    spec:
      interval: 6h
      url: https://semaphoreui.github.io/charts/
  3. Add the following content to semaphore/semaphore-hr.yml:

    ---
    apiVersion: helm.toolkit.fluxcd.io/v2
    kind: HelmRelease
    metadata:
      name: semaphore-ui
      namespace: git-ops
    spec:
      interval: 6h
      chart:
        spec:
          chart: semaphore
          version: "16.2.2"
          sourceRef:
            kind: HelmRepository
            name: semaphore
            namespace: flux-system
          interval: 6h
      install:
        createNamespace: true
      upgrade:
        remediation:
          remediateLastFailure: true
      values:
        image:
          repository: public.ecr.aws/semaphore/pro/server
    
        database:
          type: postgres
          host: semaphore-db-cluster.git-ops.svc.cluster.local
          port: 5432
          existingSecret: semaphore.semaphore-db-cluster.credentials.postgresql.acid.zalan.do
          passwordKey: password
          username: semaphore
    
        admin:
          create: true
          existingSecret: semaphore-admin-secret
          passwordKey: password
          fullnameKey: name
          usernameKey: username
          emailKey: email
  4. Add the following content to semaphore/semaphore-secret-tmp.yml:

    ---
    apiVersion: v1
    kind: Secret
      name: semaphore-admin-secret
      namespace: git-ops
    type: Opaque
    data:
      email: <>
      name: <>
      password: <>
      username: <>
  5. Encrypt the semaphore/semaphore-secret-tmp.yml file using Sealed Secrets and save it as semaphore/semaphore-secret.yml.

    kubeseal -o yaml < semaphore/semaphore-secret-tmp.yml > semaphore/semaphore-secret.yml && \
    rm semaphore/semaphore-secret-tmp.yml
  6. Add the following content to semaphore/semaphore-ingress.yml:

    ---
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: semaphore-ingress
      namespace: git-ops
      annotations:
        cert-manager.io/cluster-issuer: letsencrypt-cloudflare
        traefik.ingress.kubernetes.io/router.middlewares: tools-authelia@kubernetescrd
        traefik.ingress.kubernetes.io/router.entrypoints: websecure
    spec:
      ingressClassName: traefik
      tls:
      - hosts:
        - semaphore.example.com
        secretName: semaphore-tls
      rules:
      - host: semaphore.example.com
        http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: semaphore-ui
                port:
                  number: 3000
  7. Add the following content to semaphore/semaphore-db.yml:

    ---
    kind: "postgresql"
    apiVersion: "acid.zalan.do/v1"
    
    metadata:
      name: "semaphore-db-cluster"
      namespace: "git-ops"
      labels:
        team: acid
    
    spec:
      teamId: "acid"
      postgresql:
        version: "17"
      numberOfInstances: 3
      volume:
        size: "5Gi"
        storageClass: "longhorn"
      users:
        semaphore: []
      databases:
        semaphore: semaphore
      patroni:
      # needed to bypass ssl since semaphore helm chart doesn't support it
        pg_hba:
          - local all all trust
          - hostssl all +zalandos 127.0.0.1/32 pam
          - host all all 127.0.0.1/32 md5
          - hostssl all +zalandos ::1/128 pam
          - host all all ::1/128 md5
          - local replication standby trust
          - hostssl replication standby all md5
          - hostssl all +zalandos all pam
          - host all all all md5
  8. Commit and push the files to your git repository. Flux will automatically deploy Semaphore to your Kubernetes cluster.