Skip to content
h8lio

GitOps repository

Every Solution is backed by a Git repository holding the Kubernetes manifests of its services. That repository is the source of truth: what it contains is what runs in your clusters, and every deployment is a commit you can read, audit and revert.

The platform creates the repository for you and writes the initial manifests. From that point on it is a normal Git repository owned by your organization: you edit it, review it, and push to it like any other.

One folder per Solution, one subfolder per service, and a Kustomize base with one overlay per environment:

{solution}/
{service}/
base/ shared manifests
deployment.yaml
service.yaml
kustomization.yaml
overlays/
{environment}/ per-environment patches
kustomization.yaml

The deployment engine watches overlays/{environment} for each cell of the Solution matrix. That overlay pulls in base/ through resources: - ../../base, then applies its own patches: the target namespace, the image tag, and anything else you declare there.

Two consequences worth remembering:

  • A manifest added to base/ applies to every environment of the service.
  • A manifest added to an overlay applies to that environment only. This is where anything environment-specific belongs, starting with hostnames.

Each folder name must match, character for character, the name of the Solution, the service or the environment it stands for. Those names are lowercase alphanumerics and hyphens, so the folders are too. A mismatch is silent: nothing looks for a folder that was never declared.

The platform writes manifests in two situations only: when a service is added to a Solution (its base/ is created), and when an environment is added (that overlay is created). Outside of those two events it does not touch the repository, so the files you add and the edits you make are preserved.

Keep metadata.namespace out of your manifests. The overlay sets it through its namespace: directive, which is what lets one base serve several environments.

Add the route manifest to the service folder and declare it in the matching kustomization.yaml, next to the generated resources:

base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
- ingress.yaml # your route

Exposing a hostname takes two IngressRoutes: one on the http entry point that redirects to HTTPS, one on the https entry point that carries the TLS configuration.

ingress.yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: my-service
spec:
entryPoints:
- http
routes:
- kind: Rule
match: Host(`my-service.my-namespace.h8l.io`)
middlewares:
# platform middleware, redirects to the https entry point
- name: https-redirect
namespace: traefik
services:
- name: my-service
port: 8080
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: my-service-tls
spec:
entryPoints:
- https
routes:
- kind: Rule
match: Host(`my-service.my-namespace.h8l.io`)
services:
- name: my-service
port: 8080
tls:
secretName: h8lio-tls

Replace my-namespace with your target namespace and 8080 with the port your Service exposes. Every namespace resolves under *.{namespace}.h8l.io, so no DNS record is needed for a platform hostname. For your own domain, point it at the platform first (a CNAME to edge.h8l.io, or an A record to its IP).

h8lio-tls above is a cert-manager certificate covering every hostname of your namespace. Declare it once per namespace, alongside your routes:

certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: h8lio
spec:
dnsNames:
- "*.my-namespace.h8l.io"
issuerRef:
group: cert-manager.io
kind: ClusterIssuer
name: h8lio-issuer
secretName: h8lio-tls

For a custom domain, use the letsencrypt-http01 cluster issuer instead. The Certificates guide covers both issuers, the wildcard case, and the Traefik resolver alternative.

Kustomize renders locally exactly what the platform applies. Run it on the overlay, not on the base:

Terminal window
kubectl kustomize {solution}/{service}/overlays/{environment}

If a resource you added does not appear in that output, it is missing from a kustomization.yaml and no amount of syncing will deploy it.

Once pushed, the state of each cell is visible on the Solution page of your dashboard: whether it is in sync with the repository, whether the workload is healthy, and the exact error when a manifest is rejected by the cluster.

Add the environment to the Solution from the dashboard. Its overlay is generated next to the existing ones, inheriting base/. Anything you had placed in an overlay, a hostname in particular, is specific to that overlay and has to be declared again for the new environment.