homelab/k3s/garage/garage.yaml
2026-04-18 18:28:55 -04:00

192 lines
4.7 KiB
YAML

# Garage — S3-compatible object storage, single-node
# Metadata (LMDB, mmap-heavy → NFS-hostile) on local-path PVC
# Data blobs on NAS via nas-nfs (subdir "garage" on /volume1/samantha-private)
# Anti-affinity excludes game-worker-hdd (slow spinning disk)
#
# Ports (NodePort only for S3 API):
# 3900 — S3 API → NodePort 32390
# 3901 — inter-node RPC (not exposed; single-node)
# 3902 — S3 web hosting (not exposed for now)
# 3903 — admin API (bound to 127.0.0.1 inside pod; kubectl exec to use)
#
# Deploy:
# kubectl create secret generic garage-secret \
# --from-literal=rpc-secret="$(openssl rand -hex 32)" \
# --from-literal=admin-token="$(openssl rand -hex 32)" \
# --from-literal=metrics-token="$(openssl rand -hex 32)"
# kubectl apply -f garage.yaml
#
# First-time layout init (run once after pod is Ready):
# kubectl exec -n default deploy/garage -- garage status
# # copy the node ID from the output, then:
# kubectl exec -n default deploy/garage -- \
# garage layout assign -z dc1 -c 500G <node-id>
# kubectl exec -n default deploy/garage -- \
# garage layout apply --version 1
---
apiVersion: v1
kind: ConfigMap
metadata:
name: garage-config
data:
garage.toml: |
metadata_dir = "/meta"
data_dir = "/data"
db_engine = "lmdb"
replication_factor = 1
consistency_mode = "consistent"
rpc_bind_addr = "[::]:3901"
rpc_public_addr = "127.0.0.1:3901"
[s3_api]
api_bind_addr = "[::]:3900"
s3_region = "garage"
root_domain = ".s3.garage.homelab"
[s3_web]
bind_addr = "[::]:3902"
root_domain = ".page.sjasoft.com"
index = "index.html"
[admin]
api_bind_addr = "[::]:3903"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: garage-meta-pvc
spec:
accessModes: [ReadWriteOnce]
storageClassName: local-path
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: garage-data-pvc
annotations:
nfs.io/storage-path: "garage"
spec:
accessModes: [ReadWriteMany]
storageClassName: nas-nfs
resources:
requests:
storage: 500Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: garage
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: garage
template:
metadata:
labels:
app: garage
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- game-worker-hdd
containers:
- name: garage
image: dxflrs/garage:v2.3.0
command: ["/garage"]
args: ["server"]
env:
- name: GARAGE_RPC_SECRET
valueFrom:
secretKeyRef:
name: garage-secret
key: rpc-secret
- name: GARAGE_ADMIN_TOKEN
valueFrom:
secretKeyRef:
name: garage-secret
key: admin-token
- name: GARAGE_METRICS_TOKEN
valueFrom:
secretKeyRef:
name: garage-secret
key: metrics-token
- name: GARAGE_CONFIG_FILE
value: /etc/garage/garage.toml
ports:
- containerPort: 3900
name: s3
- containerPort: 3901
name: rpc
- containerPort: 3902
name: web
- containerPort: 3903
name: admin
volumeMounts:
- name: config
mountPath: /etc/garage
- name: meta
mountPath: /meta
- name: data
mountPath: /data
volumes:
- name: config
configMap:
name: garage-config
- name: meta
persistentVolumeClaim:
claimName: garage-meta-pvc
- name: data
persistentVolumeClaim:
claimName: garage-data-pvc
---
apiVersion: v1
kind: Service
metadata:
name: garage
spec:
selector:
app: garage
ports:
- name: s3
port: 3900
targetPort: 3900
nodePort: 32390
- name: web
port: 3902
targetPort: 3902
type: NodePort
---
# Admin API — ClusterIP only (not publicly reachable).
# Use via: kubectl port-forward -n default svc/garage-admin 3903:3903
# Then hit http://localhost:3903 with bearer $(pass show homelab/GARAGE_ADMIN_TOKEN)
apiVersion: v1
kind: Service
metadata:
name: garage-admin
spec:
selector:
app: garage
ports:
- name: admin
port: 3903
targetPort: 3903
type: ClusterIP