## Summary - Rename `date-modified` -> `modified` in all 80 docs and the `docs-check-frontmatter` task Quartz's `CreatedModifiedDate` plugin recognizes `modified`, `lastmod`, `updated`, and `last-modified` — but not `date-modified`. The wrong field name caused Quartz to ignore frontmatter dates entirely and fall through to filesystem timestamps (UTC inside Dagger), showing Feb 12 on pages built late on Feb 11 PST. ## Test plan - [x] `mise run docs-check-frontmatter` passes - [ ] Kick off docs release after merge — verify rendered dates match frontmatter values Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/158
5.6 KiB
| title | modified | tags | |||
|---|---|---|---|---|---|
| Adding a Service | 2026-02-07 |
|
Adding an ArgoCD-Managed Service
Audiences: Contributor, Replicator
This tutorial walks through deploying a new service to BlumeOps via ArgoCD, including ingress configuration, homepage integration, and observability setup.
Prerequisites
- Access to the tailscale network
kubectlconfigured withminikube-indricontextargocdCLI installed (via Brewfile:brew bundle)
Overview
Adding a service involves:
- Creating Kubernetes manifests
- Creating an ArgoCD Application
- Configuring Tailscale ingress
- Adding Homepage dashboard entry
- Setting up Grafana dashboards (optional)
Step 1: Create Manifests Directory
Create a directory for your service's Kubernetes manifests:
argocd/manifests/<service-name>/
├── deployment.yaml
├── service.yaml
├── ingress-tailscale.yaml
└── configmap.yaml # if needed
Example Deployment
# argocd/manifests/myservice/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myservice
namespace: myservice
spec:
replicas: 1
selector:
matchLabels:
app: myservice
template:
metadata:
labels:
app: myservice
spec:
containers:
- name: myservice
image: registry.ops.eblu.me/myservice:v1.0.0
ports:
- containerPort: 8080
Example Service
# argocd/manifests/myservice/service.yaml
apiVersion: v1
kind: Service
metadata:
name: myservice
namespace: myservice
spec:
selector:
app: myservice
ports:
- port: 80
targetPort: 8080
Step 2: Configure Tailscale Ingress
Create an Ingress to expose the service via Tailscale. See tailscale-operator for details.
# argocd/manifests/myservice/ingress-tailscale.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myservice
namespace: myservice
spec:
ingressClassName: tailscale
rules:
- host: myservice
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myservice
port:
number: 80
This exposes the service at https://myservice.tail8d86e.ts.net.
Step 3: Add Homepage Annotations
Add annotations to the Ingress for automatic Homepage dashboard discovery:
metadata:
annotations:
gethomepage.dev/enabled: "true"
gethomepage.dev/name: "My Service"
gethomepage.dev/group: "Apps"
gethomepage.dev/icon: "myservice.png"
gethomepage.dev/description: "Short description"
gethomepage.dev/href: "https://myservice.ops.eblu.me"
gethomepage.dev/pod-selector: "app=myservice"
Icons use Dashboard Icons format.
Step 4: Create ArgoCD Application
Create an Application manifest to tell ArgoCD about your service:
# argocd/apps/myservice.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myservice
namespace: argocd
spec:
project: default
source:
repoURL: ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git
targetRevision: main
path: argocd/manifests/myservice
destination:
server: https://kubernetes.default.svc
namespace: myservice
syncPolicy:
syncOptions:
- CreateNamespace=true
Step 5: Add Caddy Route (Optional)
If the service needs to be accessible from other pods or containers, add a Caddy route in ansible/roles/caddy/defaults/main.yml:
caddy_services:
# ... existing services ...
- name: myservice
upstream: "https://myservice.tail8d86e.ts.net"
Then run mise run provision-indri -- --tags caddy to apply.
This enables access via https://myservice.ops.eblu.me. See routing for details on when this is needed.
Step 6: Deploy
Testing on a Feature Branch
For new services, point ArgoCD at your feature branch first:
# Sync the apps application to pick up your new Application
argocd app sync apps
# Point your app at the feature branch
argocd app set myservice --revision feature/your-branch
argocd app sync myservice
Verify Deployment
kubectl --context=minikube-indri -n myservice get pods
kubectl --context=minikube-indri -n myservice logs -f deployment/myservice
After PR Merge
Reset to main branch:
argocd app set myservice --revision main
argocd app sync myservice
Step 7: Add Observability (Optional)
Prometheus Metrics
If your service exposes Prometheus metrics, add scrape annotations:
# In deployment.yaml pod template
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
Grafana Dashboard
Create a ConfigMap in argocd/manifests/grafana-config/dashboards/:
apiVersion: v1
kind: ConfigMap
metadata:
name: myservice-dashboard
namespace: monitoring
labels:
grafana_dashboard: "1"
annotations:
grafana_folder: "Services"
data:
myservice.json: |
{ ... dashboard JSON ... }
See grafana for dashboard provisioning details.
Checklist
- Manifests created in
argocd/manifests/<service>/ - ArgoCD Application created in
argocd/apps/ - Tailscale Ingress configured
- Homepage annotations added
- Caddy route added (if needed for pod access)
- Feature branch tested via ArgoCD
- Metrics/dashboard configured (if applicable)
- PR created and reviewed
- Reset to main after merge
Related
- argocd - GitOps platform
- tailscale-operator - Kubernetes ingress
- routing - Service routing options
- grafana - Dashboard configuration
- apps - Application registry