Compare commits

..

12 Commits

27 changed files with 856 additions and 502 deletions

43
flake.lock generated
View File

@@ -18,49 +18,13 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_2": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"krew2nix": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1716272275,
"narHash": "sha256-JWDyPhAJp263EVVsGrKwrJU+xdDReHsDmSe7A190/Cg=",
"owner": "eigengrau",
"repo": "krew2nix",
"rev": "0c1fecaab044dba1249c5d09366891ec467b4ad2",
"type": "github"
},
"original": {
"owner": "eigengrau",
"repo": "krew2nix",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1761236834, "lastModified": 1762943920,
"narHash": "sha256-+pthv6hrL5VLW2UqPdISGuLiUZ6SnAXdd2DdUE+fV2Q=", "narHash": "sha256-ITeH8GBpQTw9457ICZBddQEBjlXMmilML067q0e6vqY=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d5faa84122bc0a1fd5d378492efce4e289f8eac1", "rev": "91c9a64ce2a84e648d0cf9671274bb9c2fb9ba60",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -73,7 +37,6 @@
"root": { "root": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"krew2nix": "krew2nix",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
} }
}, },

View File

@@ -4,14 +4,9 @@
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
krew2nix = {
url = "github:eigengrau/krew2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = { nixpkgs, flake-utils, krew2nix, ... }: flake-utils.lib.eachDefaultSystem (system: outputs = { nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system:
let let
lib = nixpkgs.lib; lib = nixpkgs.lib;
@@ -24,16 +19,12 @@
]; ];
}; };
}; };
kubectl = krew2nix.packages.${system}.kubectl;
in { in {
# Define the devShell for the current system # Define the devShell for the current system
devShell = pkgs.mkShell { devShell = pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
kubernetes-helm kubernetes-helm
(kubectl.withKrewPlugins (plugins: with plugins; [ kubectl-cnpg
cnpg
]))
nil nil
terraform terraform
tflint tflint

View File

@@ -1,5 +1,5 @@
global: global:
storageClass: longhorn-crypto storageClass: longhorn
image: image:
rootless: false rootless: false
service: service:

View File

@@ -1,13 +1,20 @@
global:
nodeSelector:
nodepool: worker
defaultSettings: defaultSettings:
defaultReplicaCount: 3 defaultReplicaCount: "3"
storageOverProvisioningPercentage: 100 storageOverProvisioningPercentage: "100"
backupCompressionMethod: "gzip" backupCompressionMethod: "gzip"
replicaSoftAntiAffinity: "true"
concurrentReplicaRebuildPerNodeLimit: "1"
replicaReplenishmentWaitInterval: "600"
disableSchdedulingOnCordonedNode: "true"
defaultBackupStore: defaultBackupStore:
backupTarget: "s3://homelab@auto/longhorn" backupTarget: "s3://longhorn-backups@auto"
backupTargetCredentialSecret: cloudflare-token backupTargetCredentialSecret: cloudflare-token
metrics: metrics:
serviceMonitor: serviceMonitor:
enabled: true enabled: false
ingress: ingress:
enabled: true enabled: true
ingressClassName: nginx-internal ingressClassName: nginx-internal

View File

@@ -1,4 +1,7 @@
controller: controller:
replicaCount: 3
nodeSelector:
nodepool: worker
ingressClassResource: ingressClassResource:
name: nginx-internal name: nginx-internal
enabled: true enabled: true
@@ -9,8 +12,31 @@ controller:
service: service:
annotations: annotations:
external-dns.alpha.kubernetes.io/hostname: "dogar.dev" external-dns.alpha.kubernetes.io/hostname: "dogar.dev"
extraVolumes:
- name: nix-cache
persistentVolumeClaim:
claimName: nix-cache
extraVolumeMounts:
- name: nix-cache
mountPath: /var/cache/nginx/nix
podSecurityContext:
fsGroup: 101
config:
proxy-buffering: "on"
proxy-ssl-server-name: "true"
http-snippet: |
# Persistent on-disk cache; lives on the PVC
proxy_cache_path /var/cache/nginx/nix levels=1:2 keys_zone=cachecache:32m max_size=120g inactive=365d use_temp_path=off;
# Only advertise cacheability for 200/302
map $status $cache_header {
200 "public";
302 "public";
default "no-cache";
}
tcp: tcp:
22: "homelab/gitea-ssh:22" 22: "homelab/gitea-ssh:22"
25565: "minecraft/monifactory-server:25565" 25565: "minecraft/monifactory-server:25565"
25566: "minecraft/gtnh-server:25565" 25566: "minecraft/gtnh-server:25565"
25567: "minecraft/tfg-server:25565" 25567: "minecraft/tfg-server:25565"
25568: "minecraft/atm10-server:25565"

View File

@@ -1,6 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
commonLabels: metadata:
app.kubernetes.io/managed-by: Kustomize labels:
app.kubernetes.io/managed-by: Kustomize
resources: resources:
- ./metallb/pool.yaml - ./metallb/pool.yaml

View File

@@ -3,7 +3,7 @@ apiVersion: metallb.io/v1beta1
kind: IPAddressPool kind: IPAddressPool
metadata: metadata:
name: pool name: pool
namespace: homelab namespace: metallb-system
spec: spec:
addresses: addresses:
- 192.168.18.192/26 - 192.168.18.192/26
@@ -12,7 +12,7 @@ apiVersion: metallb.io/v1beta1
kind: L2Advertisement kind: L2Advertisement
metadata: metadata:
name: pool name: pool
namespace: homelab namespace: metallb-system
spec: spec:
ipAddressPools: ipAddressPools:
- pool - pool

View File

@@ -11,7 +11,6 @@ type LonghornOptions = {
helm: HelmProvider; helm: HelmProvider;
}; };
name: string; name: string;
namespace: string;
}; };
export class Longhorn extends Construct { export class Longhorn extends Construct {
@@ -19,15 +18,15 @@ export class Longhorn extends Construct {
super(scope, id); super(scope, id);
const { helm, kubernetes } = options.providers; const { helm, kubernetes } = options.providers;
const namespace = "longhorn-system";
new Release(this, id, { new Release(this, id, {
name: options.name, name: options.name,
namespace: options.namespace, namespace,
provider: helm, provider: helm,
repository: "https://charts.longhorn.io", repository: "https://charts.longhorn.io",
chart: "longhorn", chart: "longhorn",
createNamespace: true, createNamespace: true,
upgradeInstall: true,
values: [ values: [
fs.readFileSync("helm/values/longhorn.values.yaml", { fs.readFileSync("helm/values/longhorn.values.yaml", {
encoding: "utf8", encoding: "utf8",
@@ -38,44 +37,19 @@ export class Longhorn extends Construct {
new Manifest(this, "recurring-backup-job", { new Manifest(this, "recurring-backup-job", {
provider: kubernetes, provider: kubernetes,
manifest: { manifest: {
apiVersion: "longhorn.io/v1beta1", apiVersion: "longhorn.io/v1beta2",
kind: "RecurringJob", kind: "RecurringJob",
metadata: { metadata: {
name: "daily-backup", name: "daily-backup",
namespace: options.namespace, namespace,
}, },
spec: { spec: {
cron: "0 0 * * *", cron: "0 0 * * *",
task: "backup", task: "backup",
retain: 30, retain: 7,
groups: ["default"],
concurrency: 3, concurrency: 3,
}, },
}, },
}); });
new Manifest(this, "longhorn-crypto-storage-class", {
provider: kubernetes,
manifest: {
kind: "StorageClass",
apiVersion: "storage.k8s.io/v1",
metadata: {
name: "longhorn-crypto",
},
provisioner: "driver.longhorn.io",
allowVolumeExpansion: true,
parameters: {
numberOfReplicas: "3",
staleReplicaTimeout: "2880", // 48 hours in minutes
encrypted: "true",
"csi.storage.k8s.io/provisioner-secret-name": "longhorn-encryption",
"csi.storage.k8s.io/provisioner-secret-namespace": options.namespace,
"csi.storage.k8s.io/node-publish-secret-name": "longhorn-encryption",
"csi.storage.k8s.io/node-publish-secret-namespace": options.namespace,
"csi.storage.k8s.io/node-stage-secret-name": "longhorn-encryption",
"csi.storage.k8s.io/node-stage-secret-namespace": options.namespace,
},
},
});
} }
} }

38
main.ts
View File

@@ -1,9 +1,10 @@
import * as dotenv from "dotenv"; import * as dotenv from "dotenv";
import { cleanEnv, str } from "envalid"; import { cleanEnv, str } from "envalid";
import { Construct } from "constructs"; import { Construct } from "constructs";
import { App, TerraformStack, S3Backend } from "cdktf"; import { App, TerraformStack, LocalBackend } from "cdktf";
import { HelmProvider } from "@cdktf/provider-helm/lib/provider"; import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider"; import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { NamespaceV1 } from "@cdktf/provider-kubernetes/lib/namespace-v1";
import { GiteaServer } from "./gitea"; import { GiteaServer } from "./gitea";
import { OnePassword } from "./1password"; import { OnePassword } from "./1password";
@@ -12,7 +13,6 @@ import { Longhorn } from "./longhorn";
import { AuthentikServer } from "./authentik"; import { AuthentikServer } from "./authentik";
import { ValkeyCluster } from "./valkey"; import { ValkeyCluster } from "./valkey";
import { CertManager } from "./cert-manager"; import { CertManager } from "./cert-manager";
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
import { Nginx } from "./nginx"; import { Nginx } from "./nginx";
import { Prometheus } from "./prometheus"; import { Prometheus } from "./prometheus";
import { MetalLB } from "./metallb"; import { MetalLB } from "./metallb";
@@ -45,15 +45,10 @@ class Homelab extends TerraformStack {
const namespace = "homelab"; const namespace = "homelab";
const ns = new Manifest(this, "namespace", { new NamespaceV1(this, "namespace", {
provider: kubernetes, provider: kubernetes,
manifest: { metadata: {
kind: "Namespace", name: namespace,
apiVersion: "v1",
metadata: {
name: namespace,
},
spec: {},
}, },
}); });
@@ -65,12 +60,10 @@ class Homelab extends TerraformStack {
}, },
}); });
longhorn.node.addDependency(ns);
new MetalLB(this, "metallb", { new MetalLB(this, "metallb", {
provider: helm, provider: helm,
name: "metallb", name: "metallb",
namespace, namespace: "metallb-system",
}); });
new OnePassword(this, "one-password", { new OnePassword(this, "one-password", {
@@ -121,7 +114,6 @@ class Homelab extends TerraformStack {
kubernetes, kubernetes,
helm, helm,
}, },
storageClass: "longhorn-crypto",
users: ["shahab", "budget-tracker", "authentik", "gitea"], users: ["shahab", "budget-tracker", "authentik", "gitea"],
primaryUser: "shahab", primaryUser: "shahab",
initSecretName: "postgres-password", initSecretName: "postgres-password",
@@ -156,21 +148,9 @@ class Homelab extends TerraformStack {
const app = new App(); const app = new App();
const stack = new Homelab(app, "homelab"); const stack = new Homelab(app, "homelab");
new S3Backend(stack, { new LocalBackend(stack, {
encrypt: true, path: "terraform.tfstate",
bucket: env.BUCKET, workspaceDir: ".",
key: "terraform.tfstate",
region: "auto",
skipCredentialsValidation: true,
skipMetadataApiCheck: true,
skipRegionValidation: true,
skipRequestingAccountId: true,
skipS3Checksum: true,
accessKey: env.R2_ACCESS_KEY_ID,
secretKey: env.R2_SECRET_ACCESS_KEY,
endpoints: {
s3: `${r2Endpoint}/${env.BUCKET}`,
},
}); });
app.synth(); app.synth();

231
media/deployment.yaml Normal file
View File

@@ -0,0 +1,231 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: server
namespace: media
spec:
replicas: 1
selector:
matchLabels:
app: server
template:
metadata:
labels:
app: server
spec:
nodeSelector:
kubernetes.io/hostname: aamil-3
containers:
- name: server
image: jellyfin/jellyfin:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8096
name: http
env:
- name: TZ
value: "Asia/Karachi"
volumeMounts:
- name: config
mountPath: /config
- name: cache
mountPath: /cache
- name: media
mountPath: /media
volumes:
- name: config
persistentVolumeClaim:
claimName: jellyfin-config
- name: cache
emptyDir: {}
- name: media
persistentVolumeClaim:
claimName: media
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonarr
namespace: media
spec:
replicas: 1
selector:
matchLabels:
app: sonarr
template:
metadata:
labels:
app: sonarr
spec:
nodeSelector:
kubernetes.io/hostname: aamil-3
containers:
- name: sonarr
image: lscr.io/linuxserver/sonarr:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8989
name: http
env:
- name: TZ
value: "Asia/Karachi"
- name: PUID
value: "1000"
- name: PGID
value: "1000"
volumeMounts:
- name: config
mountPath: /config
- name: media
mountPath: /media
- name: downloads
mountPath: /downloads
volumes:
- name: config
persistentVolumeClaim:
claimName: sonarr-config
- name: media
persistentVolumeClaim:
claimName: media
- name: downloads
persistentVolumeClaim:
claimName: downloads
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: qbittorrent
namespace: media
spec:
replicas: 1
selector:
matchLabels:
app: qbittorrent
template:
metadata:
labels:
app: qbittorrent
spec:
nodeSelector:
kubernetes.io/hostname: aamil-3
containers:
- name: qbittorrent
image: lscr.io/linuxserver/qbittorrent:latest
ports:
- containerPort: 8080 # web UI
name: http
- containerPort: 6881
name: bt
- containerPort: 6881
protocol: UDP
name: bt-udp
env:
- name: TZ
value: "Asia/Karachi"
- name: PUID
value: "1000"
- name: PGID
value: "1000"
- name: WEBUI_PORT
value: "8080"
volumeMounts:
- name: config
mountPath: /config
- name: downloads
mountPath: /downloads
volumes:
- name: config
persistentVolumeClaim:
claimName: qbittorrent-config
- name: downloads
persistentVolumeClaim:
claimName: downloads
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prowlarr
namespace: media
spec:
replicas: 1
selector:
matchLabels:
app: prowlarr
template:
metadata:
labels:
app: prowlarr
spec:
nodeSelector:
nodepool: worker
containers:
- name: prowlarr
image: lscr.io/linuxserver/prowlarr:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9696
name: http
env:
- name: TZ
value: "Asia/Karachi"
- name: PUID
value: "1000"
- name: PGID
value: "1000"
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
persistentVolumeClaim:
claimName: prowlarr-config
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: radarr
namespace: media
spec:
replicas: 1
selector:
matchLabels:
app: radarr
template:
metadata:
labels:
app: radarr
spec:
nodeSelector:
kubernetes.io/hostname: aamil-3
containers:
- name: radarr
image: lscr.io/linuxserver/radarr:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 7878
name: http
env:
- name: TZ
value: "Asia/Karachi"
- name: PUID
value: "1000"
- name: PGID
value: "1000"
volumeMounts:
- name: config
mountPath: /config
- name: media
mountPath: /media
- name: downloads
mountPath: /downloads
volumes:
- name: config
persistentVolumeClaim:
claimName: radarr-config
- name: media
persistentVolumeClaim:
claimName: media
- name: downloads
persistentVolumeClaim:
claimName: downloads

76
media/ingress.yaml Normal file
View File

@@ -0,0 +1,76 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: server
namespace: media
annotations:
# cert-manager (Cloudflare DNS-01)
cert-manager.io/cluster-issuer: cloudflare-issuer
cert-manager.io/acme-challenge-type: dns01
cert-manager.io/private-key-size: "4096"
# Jellyfin / streaming friendly nginx settings
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/use-proxy-protocol: "false"
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
spec:
ingressClassName: nginx-internal
tls:
- hosts:
- media.dogar.dev
secretName: media-tls
- hosts:
- sonarr.dogar.dev
secretName: sonarr-tls
- hosts:
- radarr.dogar.dev
secretName: radarr-tls
- hosts:
- torrent.dogar.dev
secretName: torrent-tls
rules:
- host: media.dogar.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: server
port:
number: 80
- host: sonarr.dogar.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sonarr
port:
number: 80
- host: radarr.dogar.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: radarr
port:
number: 80
- host: torrent.dogar.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: qbittorrent
port:
number: 80

5
media/namespace.yaml Normal file
View File

@@ -0,0 +1,5 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: media

106
media/pvc.yaml Normal file
View File

@@ -0,0 +1,106 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jellyfin-config
namespace: media
labels:
recurring-job.longhorn.io/source: "enabled"
recurring-job.longhorn.io/backup: "enabled"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: media
namespace: media
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 512Gi
storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sonarr-config
namespace: media
labels:
recurring-job.longhorn.io/source: "enabled"
recurring-job.longhorn.io/backup: "enabled"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 512Mi
storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: qbittorrent-config
namespace: media
labels:
recurring-job.longhorn.io/source: "enabled"
recurring-job.longhorn.io/backup: "enabled"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 512Mi
storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: downloads
namespace: media
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 450Gi
storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: prowlarr-config
namespace: media
labels:
recurring-job.longhorn.io/source: "enabled"
recurring-job.longhorn.io/backup: "enabled"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 512Mi
storageClassName: longhorn
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: radarr-config
namespace: media
labels:
recurring-job.longhorn.io/source: "enabled"
recurring-job.longhorn.io/backup: "enabled"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 512Mi
storageClassName: longhorn

69
media/service.yaml Normal file
View File

@@ -0,0 +1,69 @@
---
apiVersion: v1
kind: Service
metadata:
name: server
namespace: media
spec:
selector:
app: server
ports:
- name: http
port: 80
targetPort: 8096
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: sonarr
namespace: media
spec:
selector:
app: sonarr
ports:
- name: http
port: 80
targetPort: 8989
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: qbittorrent
namespace: media
spec:
selector:
app: qbittorrent
ports:
- name: http
port: 80
targetPort: 8080
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: prowlarr
namespace: media
spec:
selector:
app: prowlarr
ports:
- name: http
port: 80
targetPort: 9696
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: radarr
namespace: media
spec:
selector:
app: radarr
ports:
- name: http
port: 80
targetPort: 7878

View File

@@ -5,7 +5,6 @@ metadata:
name: monifactory-data name: monifactory-data
namespace: minecraft namespace: minecraft
spec: spec:
storageClassName: longhorn-crypto
accessModes: accessModes:
- ReadWriteMany - ReadWriteMany
resources: resources:
@@ -18,7 +17,6 @@ metadata:
name: gtnh-data name: gtnh-data
namespace: minecraft namespace: minecraft
spec: spec:
storageClassName: longhorn-crypto
accessModes: accessModes:
- ReadWriteMany - ReadWriteMany
resources: resources:
@@ -31,7 +29,21 @@ metadata:
name: tfg-data name: tfg-data
namespace: minecraft namespace: minecraft
spec: spec:
storageClassName: longhorn-crypto accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: atm10-data
namespace: minecraft
labels:
recurring-job.longhorn.io/source: "enabled"
recurring-job.longhorn.io/backup: "enabled"
spec:
accessModes: accessModes:
- ReadWriteMany - ReadWriteMany
resources: resources:

View File

@@ -43,3 +43,18 @@ spec:
port: 25565 port: 25565
selector: selector:
app: tfg-server app: tfg-server
---
apiVersion: v1
kind: Service
metadata:
name: atm10-server
namespace: minecraft
labels:
app: atm10-server
spec:
type: ClusterIP
ports:
- name: atm10
port: 25565
selector:
app: atm10-server

View File

@@ -5,6 +5,7 @@ metadata:
name: monifactory-server name: monifactory-server
namespace: minecraft namespace: minecraft
spec: spec:
serviceName: monifactory-server
selector: selector:
matchLabels: matchLabels:
app: monifactory-server app: monifactory-server
@@ -13,6 +14,8 @@ spec:
labels: labels:
app: monifactory-server app: monifactory-server
spec: spec:
nodeSelector:
nodepool: worker
containers: containers:
- name: monifactory-server - name: monifactory-server
image: itzg/minecraft-server:java17 image: itzg/minecraft-server:java17
@@ -37,7 +40,7 @@ spec:
- name: INIT_MEMORY - name: INIT_MEMORY
value: 4G value: 4G
- name: MAX_MEMORY - name: MAX_MEMORY
value: 8G value: 12G
- name: ALLOW_FLIGHT - name: ALLOW_FLIGHT
value: "TRUE" value: "TRUE"
- name: ENABLE_ROLLING_LOGS - name: ENABLE_ROLLING_LOGS
@@ -53,7 +56,7 @@ spec:
memory: "4Gi" memory: "4Gi"
limits: limits:
cpu: 8 cpu: 8
memory: "8Gi" memory: "12Gi"
volumeMounts: volumeMounts:
- name: monifactory-data - name: monifactory-data
mountPath: /data mountPath: /data
@@ -68,6 +71,7 @@ metadata:
name: gtnh-server name: gtnh-server
namespace: minecraft namespace: minecraft
spec: spec:
serviceName: gtnh-server
selector: selector:
matchLabels: matchLabels:
app: gtnh-server app: gtnh-server
@@ -76,6 +80,8 @@ spec:
labels: labels:
app: gtnh-server app: gtnh-server
spec: spec:
nodeSelector:
nodepool: worker
containers: containers:
- name: gtnh-server - name: gtnh-server
image: itzg/minecraft-server:java25 image: itzg/minecraft-server:java25
@@ -97,7 +103,7 @@ spec:
- name: SKIP_GENERIC_PACK_UPDATE_CHECK - name: SKIP_GENERIC_PACK_UPDATE_CHECK
value: "true" value: "true"
- name: MEMORY - name: MEMORY
value: 6G value: 12G
- name: JVM_OPTS - name: JVM_OPTS
value: "-Dfml.readTimeout=180 -Dfml.queryResult=confirm @java9args.txt" value: "-Dfml.readTimeout=180 -Dfml.queryResult=confirm @java9args.txt"
- name: CUSTOM_JAR_EXEC - name: CUSTOM_JAR_EXEC
@@ -112,7 +118,7 @@ spec:
resources: resources:
limits: limits:
cpu: 8 cpu: 8
memory: "8Gi" memory: "12Gi"
volumeMounts: volumeMounts:
- name: gtnh-data - name: gtnh-data
mountPath: /data mountPath: /data
@@ -127,6 +133,7 @@ metadata:
name: tfg-server name: tfg-server
namespace: minecraft namespace: minecraft
spec: spec:
serviceName: tfg-server
selector: selector:
matchLabels: matchLabels:
app: tfg-server app: tfg-server
@@ -135,6 +142,8 @@ spec:
labels: labels:
app: tfg-server app: tfg-server
spec: spec:
nodeSelector:
nodepool: worker
containers: containers:
- name: tfg-server - name: tfg-server
image: itzg/minecraft-server:java17 image: itzg/minecraft-server:java17
@@ -159,7 +168,7 @@ spec:
- name: INIT_MEMORY - name: INIT_MEMORY
value: 2G value: 2G
- name: MAX_MEMORY - name: MAX_MEMORY
value: 8G value: 12G
- name: ALLOW_FLIGHT - name: ALLOW_FLIGHT
value: "TRUE" value: "TRUE"
- name: ENABLE_ROLLING_LOGS - name: ENABLE_ROLLING_LOGS
@@ -175,7 +184,7 @@ spec:
memory: "2Gi" memory: "2Gi"
limits: limits:
cpu: 6 cpu: 6
memory: "9Gi" memory: "12Gi"
volumeMounts: volumeMounts:
- name: tfg-data - name: tfg-data
mountPath: /data mountPath: /data
@@ -183,3 +192,71 @@ spec:
- name: tfg-data - name: tfg-data
persistentVolumeClaim: persistentVolumeClaim:
claimName: tfg-data claimName: tfg-data
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: atm10-server
namespace: minecraft
spec:
serviceName: atm10-server
selector:
matchLabels:
app: atm10-server
template:
metadata:
labels:
app: atm10-server
spec:
nodeSelector:
nodepool: worker
containers:
- name: atm10-server
image: itzg/minecraft-server:java21
env:
- name: EULA
value: "TRUE"
- name: MODE
value: "survival"
- name: MODPACK_PLATFORM
value: "AUTO_CURSEFORGE"
- name: CF_API_KEY
valueFrom:
secretKeyRef:
name: curseforge
key: credential
- name: CF_PAGE_URL
value: "https://www.curseforge.com/minecraft/modpacks/all-the-mods-10/files/7121777"
- name: VERSION
value: "1.21.1"
- name: INIT_MEMORY
value: 2G
- name: MAX_MEMORY
value: 15G
- name: ALLOW_FLIGHT
value: "TRUE"
- name: ENABLE_ROLLING_LOGS
value: "TRUE"
- name: USE_MEOWICE_FLAGS
value: "TRUE"
- name: CF_OVERRIDES_EXCLUSIONS
value: |
# Not applicable for server side
shaderpacks/**
ports:
- name: minecraft
containerPort: 25565
resources:
requests:
cpu: 2
memory: "2Gi"
limits:
cpu: 6
memory: "16Gi"
volumeMounts:
- name: atm10-data
mountPath: /data
volumes:
- name: atm10-data
persistentVolumeClaim:
claimName: atm10-data

View File

@@ -3,6 +3,8 @@ import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
import { Release } from "@cdktf/provider-helm/lib/release"; import { Release } from "@cdktf/provider-helm/lib/release";
import { Construct } from "constructs"; import { Construct } from "constructs";
import { NixCache } from "./nix-cache";
type NginxOptions = { type NginxOptions = {
provider: HelmProvider; provider: HelmProvider;
name: string; name: string;
@@ -24,5 +26,11 @@ export class Nginx extends Construct {
}), }),
], ],
}); });
new NixCache(this, "nix-cache", {
namespace: options.namespace,
host: "nix.dogar.dev",
ingressClassName: "nginx-internal",
});
} }
} }

105
nginx/nix-cache.ts Normal file
View File

@@ -0,0 +1,105 @@
import { Construct } from "constructs";
import { ServiceV1 } from "@cdktf/provider-kubernetes/lib/service-v1";
import { IngressV1 } from "@cdktf/provider-kubernetes/lib/ingress-v1";
import { PersistentVolumeClaimV1 } from "@cdktf/provider-kubernetes/lib/persistent-volume-claim-v1";
export interface NixCacheProps {
namespace: string;
host: string;
ingressClassName?: string;
externalName?: string;
}
export class NixCache extends Construct {
constructor(scope: Construct, id: string, props: NixCacheProps) {
super(scope, id);
const {
namespace,
host,
ingressClassName: ingressClass = "nginx-internal",
externalName: upstreamHost = "cache.nixos.org",
} = props;
// 1) ExternalName Service -> cache.nixos.org
new ServiceV1(this, "nixcache-upstream-svc", {
metadata: {
name: "nixcache-upstream",
namespace,
},
spec: {
type: "ExternalName",
externalName: upstreamHost,
},
});
// 2) Ingress that targets the ExternalName Service over HTTPS:443
new IngressV1(this, "nixcache-ingress", {
metadata: {
name: "nix-cache",
namespace,
annotations: {
// Use the cache zone defined in controller.config.http-snippet
"nginx.ingress.kubernetes.io/proxy-cache": "cachecache",
"nginx.ingress.kubernetes.io/proxy-cache-valid": "200 302 60d",
"nginx.ingress.kubernetes.io/proxy-cache-lock": "true",
"nginx.ingress.kubernetes.io/proxy-buffering": "on",
// Upstream is HTTPS with SNI and a fixed Host header
"nginx.ingress.kubernetes.io/backend-protocol": "HTTPS",
"nginx.ingress.kubernetes.io/proxy-ssl-server-name": "true",
"nginx.ingress.kubernetes.io/upstream-vhost": upstreamHost,
// Use cert-manager to provision TLS certs via Cloudflare
"cert-manager.io/cluster-issuer": "cloudflare-issuer",
"cert-manager.io/acme-challenge-type": "dns01",
"cert-manager.io/private-key-size": "4096",
},
},
spec: {
ingressClassName: ingressClass,
rule: [
{
host,
http: {
path: [
{
path: "/",
pathType: "Prefix",
backend: {
service: {
name: "nixcache-upstream",
port: { number: 443 },
},
},
},
],
},
},
],
tls: [
{
hosts: [host],
secretName: "nix-cache-tls",
},
],
},
});
// 3) PersistentVolumeClaim for caching
new PersistentVolumeClaimV1(this, "nix-cache-pvc", {
metadata: {
name: "nix-cache",
namespace,
},
spec: {
accessModes: ["ReadWriteMany"],
resources: {
requests: {
storage: "128Gi",
},
},
},
});
}
}

View File

@@ -1,145 +0,0 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{ pkgs, meta, ... }:
{
imports = [ ./hardware-configuration.nix ];
nix = {
settings = {
require-sigs = false;
experimental-features = [ "nix-command" "flakes" ];
};
};
# Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
networking.hostName = meta.hostname; # Define your hostname.
# Pick only one of the below networking options.
networking.networkmanager.enable = true;
networking.interfaces.enp1s0.ipv4.addresses = [
{
address = (
if meta.hostname == "homelab-0" then "192.168.18.10"
else if meta.hostname == "homelab-1" then "192.168.18.11"
else if meta.hostname == "homelab-2" then "192.168.18.12"
else throw "Unknown hostname"
);
prefixLength = 24;
}
];
networking.defaultGateway = "192.168.18.1";
networking.nameservers = [
"192.168.18.250"
"1.1.1.1"
];
# Set your time zone.
time.timeZone = "Asia/Karachi";
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
console = {
font = "Lat2-Terminus16";
keyMap = "us";
};
# Fixes for longhorn
systemd.tmpfiles.rules = [
"L+ /usr/local/bin - - - - /run/current-system/sw/bin/"
];
virtualisation.docker.logDriver = "json-file";
services.k3s = {
enable = true;
role = "server";
tokenFile = /var/lib/rancher/k3s/server/token;
extraFlags = toString ([
"--write-kubeconfig-mode \"0644\""
"--cluster-init"
"--disable servicelb"
"--disable traefik"
"--disable local-storage"
] ++ (if meta.hostname == "homelab-0" then [] else [
"--server https://192.168.18.10:6443"
]));
clusterInit = (meta.hostname == "homelab-0");
};
services.openiscsi = {
enable = true;
name = "iqn.2016-04.com.open-iscsi:${meta.hostname}";
};
# Define a user account. Don't forget to set a password with passwd.
users.users.shahab = {
isNormalUser = true;
extraGroups = [ "wheel" ]; # Enable sudo for the user.
packages = with pkgs; [
tree
cloudflared
];
# Created using mkpasswd
hashedPassword = "$6$.ZlYnf2cZph4tCbM$E/JJUDirRV8MZrgX4Rh.Pi1q95tev1ZxcKjPA1I.uURv56qoWcC39MJWO9S2T5MlkPVbSLGiM8Ihfz9mERImo/";
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGD/V4jLpuk7uAovkbHFr6uulfBKZmsH+BqmXIR2aYD0"
];
};
security.sudo.extraRules = [
{
users = ["shahab"];
commands = [
{ command = "ALL"; options = ["NOPASSWD"]; }
];
}
];
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = with pkgs; [
neovim
k3s
cifs-utils
nfs-utils
git
];
# List services that you want to enable:
# Enable the OpenSSH daemon.
services.openssh.enable = true;
# Open ports in the firewall.
# networking.firewall.allowedTCPPorts = [ 80 ];
# networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether.
networking.firewall.enable = false;
# Copy the NixOS configuration file and link it from the resulting system
# (/run/current-system/configuration.nix). This is useful in case you
# accidentally delete configuration.nix.
# system.copySystemConfiguration = true;
# This option defines the first version of NixOS you have installed on this particular machine,
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
#
# Most users should NEVER change this value after the initial install, for any reason,
# even if you've upgraded your system to a new NixOS release.
#
# This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
# so changing it will NOT upgrade your system.
#
# This value being lower than the current NixOS release does NOT mean your system is
# out of date, out of support, or vulnerable.
#
# Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
# and migrated your data accordingly.
#
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
system.stateVersion = "24.05"; # Did you read the comment?
}

View File

@@ -1,45 +0,0 @@
{
disko.devices = {
disk = {
vdb = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
priority = 1;
name = "ESP";
start = "1M";
end = "128M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
luks = {
size = "100%";
content = {
name = "crypted";
type = "luks";
passwordFile = "/tmp/secret.key";
settings = {
allowDiscards = true;
crypttabExtraOpts =
[ "fido2-device=auto" "token-timeout=10" ];
};
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
};
}

View File

@@ -1,48 +0,0 @@
{
"nodes": {
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1758287904,
"narHash": "sha256-IGmaEf3Do8o5Cwp1kXBN1wQmZwQN3NLfq5t4nHtVtcU=",
"owner": "nix-community",
"repo": "disko",
"rev": "67ff9807dd148e704baadbd4fd783b54282ca627",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1759994382,
"narHash": "sha256-wSK+3UkalDZRVHGCRikZ//CyZUJWDJkBDTQX1+G77Ow=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5da4a26309e796daa7ffca72df93dbe53b8164c7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"disko": "disko",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View File

@@ -1,37 +0,0 @@
{
description = "Homelab NixOS Flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
# Disko
disko = {
url = "github:nix-community/disko";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, disko, ... }: let
nodes = [
"homelab-0"
"homelab-1"
"homelab-2"
];
in {
nixosConfigurations = builtins.listToAttrs (map (name: {
name = name;
value = nixpkgs.lib.nixosSystem {
specialArgs = {
meta = { hostname = name; };
};
system = "x86_64-linux";
modules = [
# Modules
disko.nixosModules.disko
./hardware-configuration.nix
./disko-config.nix
./configuration.nix
];
};
}) nodes);
};
}

View File

@@ -1,24 +0,0 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, modulesPath, ... }:
{
imports = [(modulesPath + "/installer/scan/not-detected.nix")];
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault false;
networking.interfaces.enp1s0.useDHCP = lib.mkDefault false;
# networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

54
package-lock.json generated
View File

@@ -75,9 +75,9 @@
} }
}, },
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0", "version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -121,13 +121,14 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "24.0.3", "version": "24.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
"integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==", "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"undici-types": "~7.8.0" "undici-types": "~7.16.0"
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
@@ -173,6 +174,7 @@
"semver" "semver"
], ],
"license": "MPL-2.0", "license": "MPL-2.0",
"peer": true,
"dependencies": { "dependencies": {
"archiver": "7.0.1", "archiver": "7.0.1",
"json-stable-stringify": "1.2.1", "json-stable-stringify": "1.2.1",
@@ -1250,7 +1252,8 @@
"version": "10.4.2", "version": "10.4.2",
"resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz", "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz",
"integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==", "integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==",
"license": "Apache-2.0" "license": "Apache-2.0",
"peer": true
}, },
"node_modules/create-require": { "node_modules/create-require": {
"version": "1.1.1", "version": "1.1.1",
@@ -1270,9 +1273,9 @@
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "16.5.0", "version": "16.6.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@@ -1282,15 +1285,15 @@
} }
}, },
"node_modules/envalid": { "node_modules/envalid": {
"version": "8.0.0", "version": "8.1.0",
"resolved": "https://registry.npmjs.org/envalid/-/envalid-8.0.0.tgz", "resolved": "https://registry.npmjs.org/envalid/-/envalid-8.1.0.tgz",
"integrity": "sha512-PGeYJnJB5naN0ME6SH8nFcDj9HVbLpYIfg1p5lAyM9T4cH2lwtu2fLbozC/bq+HUUOIFxhX/LP0/GmlqPHT4tQ==", "integrity": "sha512-OT6+qVhKVyCidaGoXflb2iK1tC8pd0OV2Q+v9n33wNhUJ+lus+rJobUj4vJaQBPxPZ0vYrPGuxdrenyCAIJcow==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"tslib": "2.6.2" "tslib": "2.8.1"
}, },
"engines": { "engines": {
"node": ">=8.12" "node": ">=18"
} }
}, },
"node_modules/make-error": { "node_modules/make-error": {
@@ -1345,17 +1348,18 @@
} }
}, },
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.6.2", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD" "license": "0BSD"
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.8.3", "version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -1365,9 +1369,9 @@
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "7.8.0", "version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },

View File

@@ -11,7 +11,6 @@ type PostgresClusterOptions = {
}; };
name: string; name: string;
namespace: string; namespace: string;
storageClass: string;
users: string[]; users: string[];
primaryUser: string; primaryUser: string;
initSecretName: string; initSecretName: string;
@@ -31,11 +30,13 @@ export class PostgresCluster extends Construct {
chart: "cloudnative-pg", chart: "cloudnative-pg",
name: "postgres-system", name: "postgres-system",
namespace: "cnpg-system", namespace: "cnpg-system",
createNamespace: true,
}); });
const destinationPath = "s3://homelab/"; const destinationPath = "s3://postgres-backups/";
const endpointURL = options.backupR2EndpointURL; const endpointURL = options.backupR2EndpointURL;
const barmanStoreName = "r2-postgres-backup-store"; const barmanStoreName = "r2-postgres-backup-store";
const backupServerName = `${options.name}-backup`;
const barmanConfiguration = { const barmanConfiguration = {
destinationPath, destinationPath,
@@ -49,6 +50,16 @@ export class PostgresCluster extends Construct {
name: "cloudflare-token", name: "cloudflare-token",
key: "secret_access_key", key: "secret_access_key",
}, },
region: {
name: "cloudflare-token",
key: "AWS_REGION",
},
},
wal: {
compression: "gzip",
},
data: {
compression: "gzip",
}, },
}; };
@@ -62,11 +73,9 @@ export class PostgresCluster extends Construct {
name: barmanStoreName, name: barmanStoreName,
}, },
spec: { spec: {
retentionPolicy: "15d",
configuration: { configuration: {
...barmanConfiguration, ...barmanConfiguration,
wal: {
compression: "gzip",
},
}, },
}, },
}, },
@@ -147,22 +156,6 @@ export class PostgresCluster extends Construct {
}, },
}); });
// Secret for server certificate
new Manifest(this, "server-ca-cert-secret", {
provider: kubernetes,
manifest: {
apiVersion: "v1",
kind: "Secret",
metadata: {
name: certNames.server,
namespace: options.namespace,
labels: {
"cnpg.io/reload": "",
},
},
},
});
// Server certificate // Server certificate
new Manifest(this, "server-cert", { new Manifest(this, "server-cert", {
provider: kubernetes, provider: kubernetes,
@@ -332,7 +325,7 @@ export class PostgresCluster extends Construct {
postgresql: { postgresql: {
parameters: { parameters: {
archive_mode: "on", archive_mode: "on",
archive_timeout: "5min", archive_timeout: "60min",
checkpoint_timeout: "10min", checkpoint_timeout: "10min",
checkpoint_completion_target: "0.7", checkpoint_completion_target: "0.7",
dynamic_shared_memory_type: "posix", dynamic_shared_memory_type: "posix",
@@ -346,16 +339,17 @@ export class PostgresCluster extends Construct {
logging_collector: "on", logging_collector: "on",
max_parallel_workers: "32", max_parallel_workers: "32",
max_replication_slots: "32", max_replication_slots: "32",
max_wal_size: "768MB",
max_worker_processes: "32", max_worker_processes: "32",
max_slot_wal_keep_size: "256MB", max_slot_wal_keep_size: "256MB",
max_wal_size: "512MB",
min_wal_size: "128MB", min_wal_size: "128MB",
shared_memory_type: "mmap", shared_memory_type: "mmap",
shared_preload_libraries: "", shared_preload_libraries: "",
ssl_max_protocol_version: "TLSv1.3", ssl_max_protocol_version: "TLSv1.3",
ssl_min_protocol_version: "TLSv1.3", ssl_min_protocol_version: "TLSv1.3",
wal_compression: "on",
wal_keep_size: "128MB", wal_keep_size: "128MB",
wal_level: "logical", wal_level: "replica",
wal_log_hints: "on", wal_log_hints: "on",
wal_receiver_timeout: "5s", wal_receiver_timeout: "5s",
wal_sender_timeout: "5s", wal_sender_timeout: "5s",
@@ -368,31 +362,16 @@ export class PostgresCluster extends Construct {
plugins: [ plugins: [
{ {
name: "barman-cloud.cloudnative-pg.io", name: "barman-cloud.cloudnative-pg.io",
enabled: true,
isWALArchiver: true, isWALArchiver: true,
parameters: { parameters: {
barmanObjectName: barmanStoreName, barmanObjectName: barmanStoreName,
serverName: backupServerName,
}, },
}, },
], ],
enableSuperuserAccess: true,
// bootstrap: {
// recovery: {
// source: "clusterBackup",
// database: "postgres",
// owner: options.primaryUser,
// secret: {
// name: options.initSecretName,
// },
// },
// },
bootstrap: { bootstrap: {
initdb: { recovery: {
database: "postgres", source: "clusterBackup",
secret: {
name: options.initSecretName,
},
postInitSQL: [`CREATE USER ${options.primaryUser} SUPERUSER;`],
}, },
}, },
externalClusters: [ externalClusters: [
@@ -402,7 +381,8 @@ export class PostgresCluster extends Construct {
name: "barman-cloud.cloudnative-pg.io", name: "barman-cloud.cloudnative-pg.io",
parameters: { parameters: {
barmanObjectName: barmanStoreName, barmanObjectName: barmanStoreName,
serverName: "postgres-cluster", serverName: backupServerName,
skipWalArchiveCheck: true,
}, },
}, },
}, },
@@ -416,6 +396,7 @@ export class PostgresCluster extends Construct {
serviceTemplate: { serviceTemplate: {
metadata: { metadata: {
name: "postgres-cluster", name: "postgres-cluster",
superuser: true,
annotations: { annotations: {
"external-dns.alpha.kubernetes.io/hostname": "external-dns.alpha.kubernetes.io/hostname":
"postgres.dogar.dev", "postgres.dogar.dev",
@@ -428,14 +409,26 @@ export class PostgresCluster extends Construct {
}, },
], ],
}, },
roles: [
{
name: options.primaryUser,
inRoles: ["postgres"],
inherit: true,
disablePassword: true,
createdb: true,
createrole: true,
login: true,
ensure: "present",
},
],
}, },
storage: { storage: {
size: "10Gi", size: "10Gi",
storageClass: options.storageClass, storageClass: "longhorn",
}, },
walStorage: { walStorage: {
size: "1Gi", size: "2Gi",
storageClass: options.storageClass, storageClass: "longhorn",
}, },
}, },
}, },
@@ -451,8 +444,18 @@ export class PostgresCluster extends Construct {
namespace: options.namespace, namespace: options.namespace,
}, },
spec: { spec: {
schedule: "0 0 0 * * *", // daily at midnight immediate: true,
// weekly midnight on Sunday
schedule: "* 0 0 * * 0",
backupOwnerReference: "self", backupOwnerReference: "self",
method: "plugin",
pluginConfiguration: {
name: "barman-cloud.cloudnative-pg.io",
parameters: {
barmanObjectName: barmanStoreName,
serverName: backupServerName,
},
},
cluster: { cluster: {
name: options.name, name: options.name,
}, },

View File

@@ -17,7 +17,7 @@ export class ValkeyCluster extends Construct {
const labels = { app: "valkey" }; const labels = { app: "valkey" };
const { provider, name, namespace } = options; const { provider, name, namespace } = options;
new DeploymentV1(this, "valkeyDeployment", { new DeploymentV1(this, "valkey-deployment", {
provider, provider,
metadata: { metadata: {
name, name,
@@ -93,7 +93,7 @@ export class ValkeyCluster extends Construct {
}, },
}); });
new ServiceV1(this, "valkeyService", { new ServiceV1(this, "valkey-service", {
provider, provider,
metadata: { metadata: {
name, name,