Phase 1b: Deploy docs hosting with Quartz (#85)

## Summary
- Add ArgoCD Application and manifests for `quartz` service
- Add `docs.ops.eblu.me` to Caddy reverse proxy configuration
- ConfigMap points to blumeops v1.0.0 release tarball
- Tailscale ingress with homepage annotations for auto-discovery

## Deployment and Testing

**Pre-deployment (container build):**
- [ ] Build and tag quartz container: `mise run container-tag-and-release quartz v1.0.0`

**K8s deployment:**
- [ ] Sync apps: `argocd app sync apps`
- [ ] Point quartz at feature branch: `argocd app set quartz --revision feature/docs-phase-1b-hosting`
- [ ] Sync quartz: `argocd app sync quartz`
- [ ] Verify pod is running: `kubectl --context=minikube-indri get pods -n quartz`
- [ ] Verify Tailscale ingress: `kubectl --context=minikube-indri get ingress -n quartz`

**Caddy deployment:**
- [ ] Dry run: `mise run provision-indri -- --tags caddy --check --diff`
- [ ] Apply: `mise run provision-indri -- --tags caddy`

**Verification:**
- [ ] Test https://docs.tail8d86e.ts.net
- [ ] Test https://docs.ops.eblu.me
- [ ] Verify homepage dashboard shows docs link

**Post-merge:**
- [ ] Reset to main: `argocd app set quartz --revision main && argocd app sync quartz`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/85
This commit is contained in:
Erich Blume 2026-02-03 10:52:20 -08:00
commit 1c86134a62
10 changed files with 152 additions and 33 deletions

View file

@ -70,6 +70,9 @@ caddy_services:
- name: hajimari
host: "go.{{ caddy_domain }}"
backend: "https://go.tail8d86e.ts.net"
- name: docs
host: "docs.{{ caddy_domain }}"
backend: "https://docs.tail8d86e.ts.net"
- name: sifaka
host: "nas.{{ caddy_domain }}"
backend: "http://sifaka:5000"

18
argocd/apps/docs.yaml Normal file
View file

@ -0,0 +1,18 @@
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: docs
namespace: argocd
spec:
project: default
source:
repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git
targetRevision: main
path: argocd/manifests/docs
destination:
server: https://kubernetes.default.svc
namespace: docs
syncPolicy:
syncOptions:
- CreateNamespace=true

View file

@ -0,0 +1,10 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: docs-config
namespace: docs
data:
# BlumeOps docs release URL
# Update this to deploy a new docs version
DOCS_RELEASE_URL: "https://forge.ops.eblu.me/eblume/blumeops/releases/download/v1.0.0/docs-v1.0.0.tar.gz"

View file

@ -0,0 +1,43 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: docs
namespace: docs
spec:
replicas: 1
selector:
matchLabels:
app: docs
template:
metadata:
labels:
app: docs
spec:
containers:
- name: docs
image: registry.ops.eblu.me/blumeops/quartz:v1.0.0
ports:
- containerPort: 80
name: http
envFrom:
- configMapRef:
name: docs-config
resources:
requests:
memory: "64Mi"
cpu: "10m"
limits:
memory: "128Mi"
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 5
periodSeconds: 10

View file

@ -0,0 +1,25 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: docs-tailscale
namespace: docs
annotations:
tailscale.com/proxy-class: "default"
gethomepage.dev/enabled: "true"
gethomepage.dev/name: "Docs"
gethomepage.dev/group: "Apps"
gethomepage.dev/icon: "mdi-book-open-page-variant"
gethomepage.dev/description: "BlumeOps Documentation"
gethomepage.dev/href: "https://docs.ops.eblu.me"
gethomepage.dev/pod-selector: "app=docs"
spec:
ingressClassName: tailscale
defaultBackend:
service:
name: docs
port:
number: 80
tls:
- hosts:
- docs

View file

@ -0,0 +1,9 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: docs
resources:
- configmap.yaml
- deployment.yaml
- service.yaml
- ingress-tailscale.yaml

View file

@ -0,0 +1,13 @@
---
apiVersion: v1
kind: Service
metadata:
name: docs
namespace: docs
spec:
selector:
app: docs
ports:
- name: http
port: 80
targetPort: 80

View file

@ -11,42 +11,11 @@ FROM nginx:alpine
# Install curl for downloading release assets
RUN apk add --no-cache curl
# Copy startup script
# Copy startup script and nginx config
COPY start.sh /start.sh
COPY default.conf /etc/nginx/conf.d/default.conf
RUN chmod +x /start.sh
# Custom nginx config for SPA routing
RUN cat > /etc/nginx/conf.d/default.conf << 'EOF'
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Enable gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA fallback - serve index.html for client-side routing
location / {
try_files $uri $uri/ $uri.html /index.html;
}
# Health check endpoint
location /healthz {
access_log off;
return 200 "ok\n";
add_header Content-Type text/plain;
}
}
EOF
EXPOSE 80
CMD ["/start.sh"]

View file

@ -0,0 +1,28 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Enable gzip compression
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA fallback - serve index.html for client-side routing
location / {
try_files $uri $uri/ $uri.html /index.html;
}
# Health check endpoint
location /healthz {
access_log off;
return 200 "ok\n";
add_header Content-Type text/plain;
}
}

View file

@ -63,6 +63,7 @@ The documentation is being restructured to follow the [Diataxis](https://diataxi
```
- [ ] Test end-to-end: commit -> build -> release -> deploy
- [ ] Set up `CHANGELOG.md` with [towncrier](https://towncrier.readthedocs.io/) using news fragments from zk cards
- [ ] Add `docs.ops.eblu.me` link to homepage dashboard
### Phase 2: Tutorials
Learning-oriented content for getting started.