Skip to content

About

Infrastructure for the local development platform. Part of hojt/homelab.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

37 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

local-platform

Infrastructure for the local development platform.

Purpose

This repository owns the local Kubernetes platform used during development.

Current platform capabilities include:

  • containerized development environment
  • Kubernetes cluster
  • local container registry
  • GitOps engine
  • HTTP and HTTPS routing through Gateway API
  • TLS certificate management
  • encrypted GitOps secret management
  • observability
  • direct local access to Gateway workloads
  • local host integration for trusted HTTPS, hostname resolution, and required kernel limits
  • task-based automation

Current implementations include:

  • Dev Containers for the development environment
  • Kind for Kubernetes
  • Podman for container operations
  • Argo CD for GitOps
  • Envoy Gateway for Gateway API
  • cert-manager for certificate management
  • Sealed Secrets for encrypted GitOps secrets
  • OpenTelemetry Collector for telemetry ingestion and processing
  • Prometheus for metrics storage and querying
  • Tempo for trace storage and querying
  • Grafana for observability exploration and visualization

Future platform capabilities may include:

  • CI pipelines
  • more complete local LoadBalancer integration
  • persistent observability storage
  • a dedicated log backend
  • richer dashboards and alerting

Alternative implementations may also be evaluated as the homelab evolves.

Examples include:

  • Flux for GitOps
  • Cilium for networking and Gateway API
  • cloud-provider-kind for local LoadBalancer integration
  • Ingress
  • Traefik
  • HAProxy
  • Istio
  • alternative OpenTelemetry-compatible observability backends

Specific tools are generally treated as implementations of platform capabilities rather than permanent architectural choices.

Prerequisites

The developer host is intentionally kept lightweight.

Required on the host:

  • Fedora Atomic
  • Podman
  • Dev Container CLI
  • tmux
  • Git
  • terminal and editor of choice

Repository-specific tooling runs inside the Dev Container.

The rootless Podman and Kind platform also requires enough per-user inotify instances on the host as the number of platform components grows.

The recommended host value is:

fs.inotify.max_user_instances = 1024

The host-side inotify.sh helper can inspect and configure this limit.

This setting is kept outside task up because it modifies the developer host.

Host
  β”‚
  β–Ό
dev.sh
  β”‚
  β–Ό
Dev Container
  β”‚
  β–Ό
Task
  β”‚
  β–Ό
scripts/

Golden Path

Inspect and configure the host inotify limit when needed:

./inotify.sh status
./inotify.sh install

Start or reconnect to the development environment:

./dev.sh

Inside the Dev Container, bring up the local platform:

task up

The platform currently consists of:

Local Container Registry
          β”‚
          β–Ό
     Kind Cluster
          β”‚
          β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β–Ό               β–Ό                β–Ό                β–Ό
     Sealed Secrets   Observability      Argo CD       Gateway/TLS
                           β”‚                              β”‚
                           β”œβ”€β”€ Prometheus                 β”œβ”€β”€ Envoy Gateway
                           β”œβ”€β”€ Tempo                      └── cert-manager
                           β”œβ”€β”€ Grafana
                           └── OTel Collector
                                  β”‚
                                  β–Ό
                              Telemetry
                                  β”‚
                                  β–Ό
                               Workloads

Inspect the running platform:

task status

Or inspect individual components:

task registry:status
task cluster:status
task argocd:status
task envoy:status
task cert-manager:status
task sealed-secrets:status
task observability:status
task prometheus:status
task tempo:status
task grafana:status
task otel:status

The desired state of workloads is maintained separately in local-environments.

After the platform lifecycle has installed Argo CD, register the desired-state applications from that repository:

task argocd:bootstrap

From that point, workload changes are reconciled from Git.

Application repositories are responsible for building and publishing the container images referenced by local-environments.

The local Gateway is reachable directly from the developer host through fixed Kind port mappings. No long-running kubectl port-forward process is required for normal workload access.

Configure the remaining host-side integration:

./trust-ca.sh install
./hosts.sh install

All three local hostnames resolve to 127.0.0.1. With the local CA trusted, the standard HTTPS entry points are:

https://example.local:8443
https://grafana.local:8443
https://argocd.local:8443

The example backend can also be reached over HTTP:

curl http://example.local:8080/api/greeting

Or over trusted HTTPS:

curl https://example.local:8443/api/greeting

When finished, tear down the local platform:

task down

Host-side inotify configuration, trust, and hostname configuration are intentionally separate from the platform lifecycle. They can be managed explicitly when needed:

./inotify.sh remove
./trust-ca.sh remove
./hosts.sh remove

The golden path is intentionally small:

host prerequisites
    β”‚
    β–Ό
 ./dev.sh
    β”‚
    β–Ό
 task up
    β”‚
    β–Ό
local-environments
    β”‚
    β–Ό
Argo CD reconciliation
    β”‚
    β–Ό
Gateway API routing + observability
    β”‚
    β–Ό
direct host access
    β”‚
    β–Ό
 workloads
    β”‚
    β–Ό
 task down

Development Environment

dev.sh is the normal entrypoint for working with this repository.

Start a new repository-specific tmux session, or reconnect to an existing one:

./dev.sh

Rebuild the Dev Container when its definition has changed:

./dev.sh rebuild

dev.sh verifies host prerequisites, starts or reuses the repository-specific tmux session, starts the Dev Container, and opens a shell inside it.

Most repository operations are exposed through Task:

task --list

Dev Container

The Dev Container provides the tooling required to develop and operate the local platform.

The normal entrypoint is ./dev.sh, but the Dev Container can also be operated manually.

Build the Dev Container:

devcontainer build \
  --workspace-folder . \
  --docker-path podman

Start it:

devcontainer up \
  --workspace-folder . \
  --docker-path podman

Open a shell inside it:

devcontainer exec \
  --workspace-folder . \
  --docker-path podman \
  bash

Personal developer configuration remains outside the repository where practical. Selected host configuration, such as Git and editor configuration, may be mounted into the Dev Container.

Platform Lifecycle

The complete local platform can normally be managed through:

task up
task status
task down

task up currently brings up the platform in dependency-aware order:

registry
   β”‚
   β–Ό
cluster
   β”‚
   β–Ό
registry integration
   β”‚
   β–Ό
Sealed Secrets key restore
   β”‚
   β–Ό
Sealed Secrets controller
   β”‚
   β–Ό
observability namespace
   β”‚
   β”œβ”€β”€ Prometheus
   β”œβ”€β”€ Tempo
   β”œβ”€β”€ Grafana
   └── OpenTelemetry Collector
   β”‚
   β–Ό
Argo CD install -> configure -> status
   β”‚
   β–Ό
Envoy Gateway
   β”‚
   β–Ό
cert-manager
   β”‚
   β–Ό
Gateway configuration

Observability starts early so platform and workload telemetry can be received as soon as producers become available. Its startup and status order is observability -> Prometheus -> Tempo -> Grafana -> OpenTelemetry Collector.

During shutdown the producer-facing OpenTelemetry Collector is removed late, followed by Grafana, Tempo, Prometheus, and finally the shared observability namespace.

The Kind cluster is created with fixed host-to-node port mappings for the local Gateway:

127.0.0.1:8080 -> Kind node :30080
127.0.0.1:8443 -> Kind node :30443

The Envoy data plane uses the corresponding fixed NodePorts, providing stable local access across clean platform bootstraps.

Sealed Secrets key material has a lifecycle independent of the disposable Kind cluster.

During task down, the complete Sealed Secrets key set is backed up before the controller and cluster are deleted. During task up, any existing key backup is restored before the Sealed Secrets controller is installed.

task down
   β”‚
   β”œβ”€β”€ sealed-secrets:backup
   β”œβ”€β”€ platform component deletion
   └── cluster:delete

task up
   β”‚
   β”œβ”€β”€ cluster:create
   β”œβ”€β”€ sealed-secrets:restore
   β”œβ”€β”€ sealed-secrets:install
   └── remaining platform bootstrap

The restore operation is re-entrant. If no key backup exists, restore is skipped and the controller creates its initial sealing key. If a backup exists, the keys are restored before the controller starts so clean cluster recreation does not create unnecessary replacement keys.

Individual components also expose lifecycle tasks for development, troubleshooting, and experimentation.

This separation keeps the common workflow simple while still allowing each platform component to be operated independently.

Kubernetes

The local platform currently uses Kind to provide Kubernetes.

The Kubernetes cluster lifecycle is owned by local-platform. Kind is the current implementation and may be replaced or complemented by other Kubernetes distributions as the homelab evolves.

Create the cluster:

task cluster:create

Show cluster status:

task cluster:status

Delete the cluster:

task cluster:delete

Useful direct inspection commands include:

kind get clusters
kubectl get nodes

The Kind node exposes fixed container ports to the developer host so the Envoy Gateway can be reached without port forwarding.

The current mapping is:

Host :8080  -> Kind node :30080
Host :8443  -> Kind node :30443

These mappings are established when the Kind node is created and therefore depend on stable NodePorts on the Envoy Gateway service.

Local Container Registry

The platform provides a local OCI registry used by workloads running in the Kind cluster.

Application repositories are responsible for building and publishing their container images. local-platform provides and operates the registry.

Create the registry:

task registry:create

Show registry status:

task registry:status

List repositories currently published to the registry:

task registry:images

List available tags for an image:

task registry:tags IMAGE=example-backend

For example:

{"name":"example-backend","tags":["0.6.0"]}

Delete the registry:

task registry:delete

An image can also be pushed manually:

podman push \
  --tls-verify=false \
  localhost:5001/example-backend:0.6.0

GitOps

The local platform includes Argo CD as the initial GitOps engine.

Argo CD is installed and operated by local-platform, while the desired state of workloads is maintained separately in the local-environments repository.

The responsibility boundary is intentionally explicit:

local-platform
  β”‚
  β”œβ”€β”€ Kubernetes
  β”œβ”€β”€ Container Registry
  β”œβ”€β”€ Argo CD
  β”œβ”€β”€ cert-manager
  β”œβ”€β”€ Sealed Secrets controller
  β”œβ”€β”€ Observability platform
  └── Gateway API
          β”‚
          β–Ό
local-environments
          β”‚
          β”œβ”€β”€ desired workload state
          └── encrypted SealedSecret resources
          β”‚
          β–Ό
     Workloads

The GitOps engine itself is considered an implementation detail. The architecture allows alternative GitOps tools, such as Flux, to be evaluated alongside or instead of Argo CD in the future.

Argo CD

Argo CD is available through the shared Gateway at https://argocd.local:8443. Browser traffic is protected by TLS terminated at Envoy. server.insecure=true applies only to the internal Envoy-to- argocd-server hop.

The platform lifecycle configures Argo CD in order:

task argocd:install
task argocd:configure
task argocd:status

Refresh Argo CD applications:

task argocd:refresh

Force a hard refresh, including cached manifests:

task argocd:refresh:hard

Delete Argo CD:

task argocd:delete

Application definitions and environment state are intentionally not maintained in this repository.

The initial Argo CD application definitions are owned by local-environments and registered after the platform lifecycle has installed Argo CD. After that registration, normal workload changes are performed through Git and reconciled by Argo CD.

Certificate Management

The local platform uses cert-manager for TLS certificate management.

The local certificate hierarchy consists of a self-signed bootstrap issuer and a locally generated root CA. The root CA is then used to issue certificates for the shared Gateway. This allows the host to trust one local CA instead of trusting individual leaf certificates.

Install cert-manager:

task cert-manager:install

Configure the local issuers and root CA:

task cert-manager:configure

Show cert-manager status:

task cert-manager:status

Export the current local root CA from Kubernetes:

task cert-manager:export-ca

The generated CA is local development infrastructure. Recreating the platform may generate a new root CA, in which case the host trust store must be updated to trust the newly exported CA.

Delete cert-manager:

task cert-manager:delete

The local CA is intended for development only. A different certificate issuer can be introduced when environments require publicly or internally trusted certificates.

Secret Management

The local platform uses Sealed Secrets for GitOps-compatible secret management.

Plaintext secret values are not stored in Git. Workload repositories instead store SealedSecret resources that contain encrypted secret data. The Sealed Secrets controller runs inside the destination cluster and materializes normal Kubernetes Secret resources there.

The responsibility boundary is:

local-platform
        β”‚
        β”œβ”€β”€ Sealed Secrets controller
        └── sealing key lifecycle
                 β”‚
                 β–Ό
local-environments
        β”‚
        └── SealedSecret
                 β”‚
                 β–Ό
        Kubernetes Secret
                 β”‚
                 β–Ό
              Workload

Sealed Secrets

Install the controller:

task sealed-secrets:install

Show controller and sealing key status:

task sealed-secrets:status

Back up the current sealing keys:

task sealed-secrets:backup

Restore backed-up sealing keys:

task sealed-secrets:restore

Delete the controller:

task sealed-secrets:delete

The sealing keys are stored by the controller as Kubernetes TLS secrets. Automatic key renewal may create additional keys over time. Old keys remain important because existing SealedSecret resources may still depend on them.

The backup therefore contains the complete current key set, not only the newest key.

Local backups are stored under:

.local/sealed-secrets/keys.yaml

The .local/ directory is excluded from Git.

The backup contains private cryptographic key material and must be treated as sensitive persistent state. The local file is sufficient for development and recovery testing, but stronger external backup protection is expected for non-local environments.

The normal platform lifecycle automatically integrates key backup and restore:

clean shutdown
    β”‚
    β–Ό
backup complete sealing key set
    β”‚
    β–Ό
delete disposable cluster

create new cluster
    β”‚
    β–Ό
restore existing sealing keys
    β”‚
    β–Ό
install Sealed Secrets controller
    β”‚
    β–Ό
existing SealedSecret ciphertext remains decryptable

This keeps the Kubernetes cluster disposable while allowing encrypted GitOps state to remain stable across cluster recreation.

The explicit backup and restore tasks remain available for troubleshooting, disaster-recovery exercises, and manual key lifecycle operations.

Observability

The local platform uses an OpenTelemetry-first observability architecture.

Application workloads export telemetry using OpenTelemetry Protocol (OTLP). The OpenTelemetry Collector provides a stable ingestion and processing layer between workloads and observability backends.

The current architecture is:

Workload
   β”‚
   β”‚ OTLP
   β–Ό
OpenTelemetry Collector
   β”‚
   β”œβ”€β”€ logs ────────► debug exporter
   β”‚
   β”œβ”€β”€ traces ──────► debug exporter
   β”‚       β”‚
   β”‚       └────────► Tempo
   β”‚                   :4317 OTLP/gRPC
   β”‚                   :3200 query API
   β”‚                         β”‚
   β”‚                         β–Ό
   β”‚                      Grafana
   β”‚
   └── metrics
          β”‚
          β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ί debug exporter
          β”‚
          β–Ό
   Prometheus exporter
        :8889
          β”‚
          β”‚ scrape
          β–Ό
      Prometheus
          β”‚
          β”‚ PromQL
          β–Ό
        Grafana

The current implementation intentionally keeps the stack small. Metrics have a storage and query backend in Prometheus. Traces are exported to both the detailed Collector debug exporter and Tempo using OTLP/gRPC. Logs remain exported only through the debug exporter, and the metrics and logs pipelines are otherwise unchanged.

All observability components currently share the observability namespace. The namespace has its own lifecycle and is not owned by any individual observability component.

OpenTelemetry Collector

The OpenTelemetry Collector receives OTLP over:

gRPC: 4317
HTTP: 4318

Install the Collector:

task otel:install

Show Collector status:

task otel:status

Inspect recently received telemetry:

kubectl \
  --context kind-local \
  --namespace observability \
  logs deployment/otel-collector \
  --since=2m

Follow telemetry live:

kubectl \
  --context kind-local \
  --namespace observability \
  logs deployment/otel-collector \
  --follow

Delete the Collector:

task otel:delete

The metrics pipeline also exposes Prometheus-compatible metrics on port 8889.

For troubleshooting, the endpoint can be inspected directly using a temporary port forward:

kubectl \
  --context kind-local \
  --namespace observability \
  port-forward service/otel-collector 8889:8889

Then:

curl --silent http://localhost:8889/metrics

The verified application trace flow is:

browser frontend span
    -> W3C trace context
    -> Quarkus backend span
    -> OpenTelemetry Collector
    -> Tempo
    -> Grafana Explore

Prometheus

Prometheus provides metrics storage and PromQL querying.

It scrapes the OpenTelemetry Collector's Prometheus exporter rather than scraping application workloads directly:

otel-collector.observability.svc:8889

Install Prometheus:

task prometheus:install

Show Prometheus status:

task prometheus:status

Delete Prometheus:

task prometheus:delete

For local exploration, expose the UI temporarily:

kubectl \
  --context kind-local \
  --namespace observability \
  port-forward service/prometheus 9090:9090

The UI is then available at:

http://localhost:9090

Example application metrics include:

http_server_requests_milliseconds_count
jvm_classes_loaded

Prometheus currently uses disposable local storage. Persistence and retention are intentionally deferred until they solve a concrete development need.

Tempo

Tempo provides the local tracing backend. It runs in monolithic, single-binary mode and receives OTLP/gRPC internally on port 4317. Its query API is available only inside the cluster on port 3200; Tempo is not exposed outside the cluster.

Tempo uses local ephemeral storage through an emptyDir mounted at /var/tempo:

WAL:          /var/tempo/wal
trace blocks: /var/tempo/blocks

Trace data is lost when the Tempo pod is recreated.

Install Tempo:

task tempo:install

Show Tempo status:

task tempo:status

Delete Tempo:

task tempo:delete

Persistent trace storage, object storage, the metrics generator, span metrics, service graphs, exemplars, and deeper Prometheus/Tempo correlation remain deferred until a concrete need is demonstrated.

Grafana

Grafana provides the initial observability user interface.

Prometheus is provisioned declaratively as Grafana's default datasource:

http://prometheus.observability.svc:9090

Tempo is provisioned declaratively as Grafana's tracing datasource:

http://tempo.observability.svc:3200

No manual datasource setup should be required after a clean platform bootstrap. Grafana is available through the shared Gateway at https://grafana.local:8443 and has declaratively provisioned Prometheus and Tempo datasources.

Install Grafana:

task grafana:install

Show Grafana status:

task grafana:status

Delete Grafana:

task grafana:delete

The current focus is manual exploration and learning through PromQL and Grafana Explore; traces can be inspected in Explore by trace ID. A temporary port-forward remains available for troubleshooting but is not the normal access path.

Dashboards will be added incrementally after useful queries and visualizations have first been understood and validated manually.

Gateway API

The local platform uses Kubernetes Gateway API for north-south HTTP and HTTPS routing.

Envoy Gateway is the initial Gateway API implementation.

local-platform owns:

  • the Envoy Gateway controller
  • the GatewayClass
  • the shared Gateway
  • the shared HTTP and HTTPS listeners
  • local TLS certificate integration
  • the local exposure of the Gateway data plane

Workload-specific routing is owned by local-environments and expressed using standard Gateway API resources such as HTTPRoute.

The shared Envoy Gateway terminates TLS for all three local HTTPS hostnames. Route attachment is restricted per listener:

  • example.local accepts routes from the default namespace
  • grafana.local accepts routes from the observability namespace
  • argocd.local accepts routes from the argocd namespace

The Grafana and Argo CD routes are platform-owned routes. Workload routes, including example.local, remain owned by local-environments.

The responsibility boundary is:

local-platform
        β”‚
        β”œβ”€β”€ Envoy Gateway controller
        β”œβ”€β”€ GatewayClass
        β”œβ”€β”€ Gateway
        β”œβ”€β”€ Grafana and Argo CD HTTPRoutes
        └── local Gateway exposure
                 β”‚
                 β–Ό
local-environments
        β”‚
        └── workload HTTPRoute
                 β”‚
                 β–Ό
              Service
                 β”‚
                 β–Ό
              Workload

Keeping workload routes based on the standard Gateway API makes it possible to experiment with alternative Gateway API implementations in the future without necessarily changing application routing manifests.

Envoy Gateway

Install Envoy Gateway and the Gateway API CRDs:

task envoy:install

Configure the local GatewayClass and Gateway:

task envoy:configure

Show Envoy Gateway status:

task envoy:status

The Envoy data plane uses stable NodePorts for the shared Gateway:

HTTP:  30080
HTTPS: 30443

The Kind control-plane node maps these ports directly to the developer host:

127.0.0.1:8080 -> 30080
127.0.0.1:8443 -> 30443

This makes the Gateway directly reachable from the host without requiring a long-running kubectl port-forward process.

With local hostname resolution configured, HTTP routes are available through:

curl http://example.local:8080/api/greeting

With the local root CA also trusted, HTTPS routes are available through:

curl https://example.local:8443/api/greeting

The NodePorts are intentionally fixed because Kind port mappings are configured when the cluster is created and therefore need stable destination ports.

The local Gateway terminates TLS using certificates managed by cert-manager.

For troubleshooting, the Gateway can still be exposed temporarily using:

task envoy:forward

Port forwarding is not required for normal local development.

Delete Envoy Gateway:

task envoy:delete

The current host ports are deliberately non-privileged ports. More transparent local access on ports 80 and 443 can be evaluated separately without changing the Gateway API routing model.

inotify exhaustion

The Envoy data plane uses inotify and may fail during startup when the host's per-user inotify instance limit is exhausted.

A typical symptom is an Envoy CrashLoopBackOff with an error similar to:

assert failure: inotify_fd_ >= 0
Consider increasing value of fs.inotify.max_user_watches and/or
fs.inotify.max_user_instances via sysctl

For this local rootless Podman and Kind environment, configure the host through:

./inotify.sh install

See Local Host Integration for details.

Local Host Integration

The Kubernetes platform is intentionally isolated from permanent host configuration, but a small amount of optional or required host integration makes local development considerably more reliable and convenient.

There are three separate concerns:

  1. provide sufficient inotify resources for the rootless container platform
  2. trust the local root CA on the Fedora host
  3. resolve local Gateway hostnames to 127.0.0.1

These operations are explicit and reversible. They are not hidden inside task up because they modify the developer host.

inotify limits

The local platform runs multiple services through rootless Podman and Kind. As the platform grows, the Fedora host's default limit for per-user inotify instances may be too low.

Envoy Gateway in particular requires an available inotify instance during data-plane startup. When the limit is exhausted, Envoy may enter CrashLoopBackOff.

The platform currently recommends:

fs.inotify.max_user_instances = 1024

Inspect the current value:

./inotify.sh status

Configure the recommended value:

./inotify.sh install

The helper persists the host configuration under /etc/sysctl.d/ and applies it immediately.

Remove the repository-managed setting and reload the remaining host sysctl configuration:

./inotify.sh remove

Only fs.inotify.max_user_instances is adjusted because that is the limit currently demonstrated to be relevant to this platform. The fs.inotify.max_user_watches limit is left unchanged until a concrete need requires otherwise.

Certificate trust

Export the root CA from the running platform inside the Dev Container:

task cert-manager:export-ca

On the Fedora host, install the exported CA:

./trust-ca.sh install

Remove it again with:

./trust-ca.sh remove

Because the root CA is generated by the local platform, a clean platform bootstrap may replace it. When that happens, export the new CA and reinstall the host trust entry.

This keeps certificate generation owned by Kubernetes while host trust remains an explicit developer-machine operation.

Hostname resolution

hosts.sh install and hosts.sh remove idempotently manage the local /etc/hosts entries for all Gateway hostnames.

Install the mapping:

./hosts.sh install

This maps:

127.0.0.1 example.local
127.0.0.1 grafana.local
127.0.0.1 argocd.local

Remove it again with:

./hosts.sh remove

The helper keeps this host-specific operation outside the Dev Container and makes the change explicit and reversible.

Local access

With hostname resolution configured:

curl http://example.local:8080/api/greeting

With both hostname resolution and the local CA installed:

curl https://example.local:8443/api/greeting

No -k, --resolve, or long-running port-forward process is required for normal Gateway access, including Grafana and Argo CD. A temporary port-forward remains an acceptable troubleshooting fallback.

The resulting local request path is:

Developer Host
      β”‚
      β”‚ example.local:8080 / :8443
      β–Ό
127.0.0.1
      β”‚
      β–Ό
Kind extraPortMappings
      β”‚
      β”œβ”€β”€ :8080  -> node :30080
      └── :8443  -> node :30443
                         β”‚
                         β–Ό
                    Envoy Gateway
                         β”‚
                         β–Ό
                     HTTPRoute
                         β”‚
                         β–Ό
                       Service
                         β”‚
                         β–Ό
                      Workload

Repository Structure

.
β”œβ”€β”€ .devcontainer/
β”œβ”€β”€ .local/
β”‚   └── sealed-secrets/
β”‚       └── keys.yaml
β”œβ”€β”€ manifests/
β”‚   β”œβ”€β”€ argocd/
β”‚   β”‚   β”œβ”€β”€ cmd-params-configmap.yaml
β”‚   β”‚   β”œβ”€β”€ httproute.yaml
β”‚   β”‚   └── kustomization.yaml
β”‚   β”œβ”€β”€ cert-manager/
β”‚   β”‚   β”œβ”€β”€ selfsigned/
β”‚   β”‚   β”‚   β”œβ”€β”€ clusterissuer.yaml
β”‚   β”‚   β”‚   └── kustomization.yaml
β”‚   β”‚   └── ...
β”‚   β”œβ”€β”€ gateway/
β”‚   β”‚   └── envoy/
β”‚   β”‚       β”œβ”€β”€ gatewayclass.yaml
β”‚   β”‚       β”œβ”€β”€ gateway.yaml
β”‚   β”‚       β”œβ”€β”€ kustomization.yaml
β”‚   β”‚       └── ...
β”‚   β”œβ”€β”€ grafana/
β”‚   β”‚   β”œβ”€β”€ configmap-datasources.yaml
β”‚   β”‚   β”œβ”€β”€ deployment.yaml
β”‚   β”‚   β”œβ”€β”€ httproute.yaml
β”‚   β”‚   β”œβ”€β”€ kustomization.yaml
β”‚   β”‚   └── service.yaml
β”‚   β”œβ”€β”€ kind/
β”‚   β”‚   └── ...
β”‚   β”œβ”€β”€ observability/
β”‚   β”‚   └── namespace.yaml
β”‚   β”œβ”€β”€ otel/
β”‚   β”‚   β”œβ”€β”€ configmap.yaml
β”‚   β”‚   β”œβ”€β”€ deployment.yaml
β”‚   β”‚   β”œβ”€β”€ kustomization.yaml
β”‚   β”‚   └── service.yaml
β”‚   β”œβ”€β”€ prometheus/
β”‚   β”‚   β”œβ”€β”€ configmap.yaml
β”‚   β”‚   β”œβ”€β”€ deployment.yaml
β”‚   β”‚   β”œβ”€β”€ kustomization.yaml
β”‚   β”‚   └── service.yaml
β”‚   └── tempo/
β”‚       β”œβ”€β”€ configmap.yaml
β”‚       β”œβ”€β”€ deployment.yaml
β”‚       β”œβ”€β”€ kustomization.yaml
β”‚       └── service.yaml
β”œβ”€β”€ scripts/
β”œβ”€β”€ Taskfile.yml
β”œβ”€β”€ dev.sh
β”œβ”€β”€ hosts.sh
β”œβ”€β”€ inotify.sh
β”œβ”€β”€ trust-ca.sh
└── README.md

The main entrypoints are deliberately few:

  • dev.sh manages the developer environment.
  • Taskfile.yml exposes the operator-facing commands.
  • scripts/ contains reusable and non-trivial platform automation.
  • manifests/ contains declarative platform configuration owned by this repository.
  • hosts.sh manages host-side local hostname resolution.
  • inotify.sh manages the host-side inotify instance limit required by the rootless local platform.
  • trust-ca.sh manages host-side trust of the local development CA.
  • .devcontainer/ defines the repository development environment.
  • .local/ contains generated local state, including sensitive Sealed Secrets key backups, and is excluded from Git.

Task provides the user-facing interface inside the Dev Container.

Reusable or non-trivial platform automation is implemented as shell scripts under scripts/. Simple commands may be kept directly in the Taskfile when doing so keeps the underlying operation visible and easy to understand.

Host-specific operations that require privileges or modify the developer machine are kept explicit rather than hidden inside task up.

Design Principles

The repository follows the broader design principles and architecture decisions documented in the homelab repository.

In particular:

  • keep the developer host lightweight
  • prefer reproducible environments
  • expose common operations through Task
  • keep repository responsibilities explicit
  • prefer open and portable technologies
  • prefer standard APIs where practical
  • use OpenTelemetry as the application-facing observability boundary
  • keep observability backends replaceable behind that boundary
  • never store plaintext workload secrets or private sealing keys in Git
  • keep recovery-critical key material independent of disposable clusters
  • evolve the platform incrementally
  • validate small capabilities manually before automating them
  • avoid abstractions before they solve a concrete problem

local-platform owns the platform itself.

Application source code belongs in application repositories, while desired environment state and workload-specific routing belong in local-environments.

About

Infrastructure for the local development platform. Part of hojt/homelab.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages