Compare commits
1
Commits
master
..
17beb901a1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17beb901a1 |
@@ -28,7 +28,9 @@ homelab-v2/
|
||||
│ ├── archmirror/
|
||||
│ ├── blog/
|
||||
│ ├── external/ # External service vars (e.g. Home Assistant)
|
||||
│ ├── firefly/
|
||||
│ ├── gitea/
|
||||
│ ├── grocy/
|
||||
│ ├── homepage/
|
||||
│ ├── immich/
|
||||
│ ├── jellyfin/
|
||||
@@ -47,11 +49,13 @@ homelab-v2/
|
||||
| Service | Description |
|
||||
|---------|-------------|
|
||||
| **Gitea** | Self-hosted Git service |
|
||||
| **Firefly III** | Personal finance manager |
|
||||
| **Immich** | Photo and video management with face recognition |
|
||||
| **Jellyfin** | Media streaming with Intel GPU hardware transcoding |
|
||||
| **Media Stack** | Sonarr, Radarr, Prowlarr, qBittorrent — automated media acquisition |
|
||||
| **Paperless-ngx** | Document management with OCR |
|
||||
| **Pi-hole** | DNS sinkhole with ad blocking and encrypted DNS via dnscrypt-proxy |
|
||||
| **Grocy** | Pantry and grocery management |
|
||||
| **LubeLogger** | Vehicle maintenance tracker |
|
||||
| **Homepage** | Dashboard aggregator |
|
||||
| **Podsync** | Podcast downloader |
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# CryptPad Deployment TODO
|
||||
|
||||
## 1. Generate OIDC secret and update both configs
|
||||
|
||||
```bash
|
||||
# Generate a random secret
|
||||
openssl rand -base64 32
|
||||
|
||||
# Hash it for Authelia
|
||||
docker run --rm authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --password 'YOUR_SECRET'
|
||||
```
|
||||
|
||||
- [+] Decrypt Authelia configmap: `cd kubernetes && sops -d -i infrastructure/controllers/authelia/configmap.sops.yaml`
|
||||
- [+] Replace `REPLACE_WITH_PBKDF2_HASH` with the generated hash
|
||||
- [+] Re-encrypt: `sops -e -i infrastructure/controllers/authelia/configmap.sops.yaml`
|
||||
- [+] Decrypt CryptPad secret: `sops -d -i app/cryptpad/secret.sops.yaml`
|
||||
- [+] Replace `REPLACE_WITH_CRYPTPAD_OIDC_SECRET` with the plaintext secret
|
||||
- [+] Re-encrypt: `sops -e -i app/cryptpad/secret.sops.yaml`
|
||||
|
||||
## 2. Fill in and encrypt backup secret
|
||||
|
||||
```bash
|
||||
cd kubernetes && sops -d -i app/cryptpad/secret-backup.sops.yaml
|
||||
```
|
||||
|
||||
- [+] Replace `REPLACE_REST_PASSWORD` with the password for the `cryptpad` htpasswd user on rest-server
|
||||
- [+] Replace `REPLACE_WITH_STRONG_PASSWORD` x2 with restic repo passwords for NAS and B2
|
||||
- [+] Replace `REPLACE_WITH_B2_KEY_ID` with Backblaze B2 application key ID
|
||||
- [+] Replace `REPLACE_WITH_B2_APPLICATION_KEY` with Backblaze B2 application key
|
||||
- [+] Re-encrypt: `sops -e -i app/cryptpad/secret-backup.sops.yaml`
|
||||
|
||||
## 3. Create NFS directories on Synology
|
||||
|
||||
SSH into Synology and run:
|
||||
|
||||
```bash
|
||||
mkdir -p /volume3/k8s-storage/cryptpad-data
|
||||
mkdir -p /volume3/k8s-storage/cryptpad-config
|
||||
```
|
||||
|
||||
- [+] Directories created
|
||||
|
||||
## 4. Set up restic repos and htpasswd user
|
||||
|
||||
```bash
|
||||
# Generate htpasswd entry and append to /volume1/docker/rest-server/config/htpasswd on Synology
|
||||
docker run --rm httpd:2-alpine htpasswd -nbB cryptpad 'YOUR_REST_PASSWORD'
|
||||
|
||||
# Init Synology repos
|
||||
restic -r "rest:http://cryptpad:PASSWORD@synology.storage.lviv:8888/cryptpad-data/" init
|
||||
restic -r "rest:http://cryptpad:PASSWORD@synology.storage.lviv:8888/cryptpad-config/" init
|
||||
|
||||
# Init B2 repos
|
||||
B2_ACCOUNT_ID=... B2_ACCOUNT_KEY=... restic -r "b2:berezovskyi-backup-homelab-cryptpad:/data/" init
|
||||
B2_ACCOUNT_ID=... B2_ACCOUNT_KEY=... restic -r "b2:berezovskyi-backup-homelab-cryptpad:/config/" init
|
||||
```
|
||||
|
||||
- [+] cryptpad htpasswd user added to rest-server
|
||||
- [+] Synology repos initialised
|
||||
- [+] B2 repos initialised
|
||||
|
||||
## 5. Commit, push and verify deployment
|
||||
|
||||
- [ ] Commit and push all changes
|
||||
- [ ] Pod comes up healthy (first start is slow: OnlyOffice download + SSO plugin clone):
|
||||
```bash
|
||||
kubectl get pods -n cryptpad -w
|
||||
kubectl logs -n cryptpad -l app=cryptpad --tail=50 -f
|
||||
```
|
||||
- [ ] Both domains resolve with valid TLS:
|
||||
- https://cryptpad.berezovskyi.dev
|
||||
- https://sandbox.cryptpad.berezovskyi.dev
|
||||
- [ ] OIDC login works: "Register with Authelia" button appears and completes successfully
|
||||
- [ ] Run a backup job manually to verify repos:
|
||||
```bash
|
||||
kubectl create job -n cryptpad --from=cronjob/cryptpad-data-backup test-backup-data
|
||||
kubectl logs -n cryptpad -l job-name=test-backup-data -f
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cryptpad-config
|
||||
namespace: cryptpad
|
||||
data:
|
||||
application_config.js: |
|
||||
(() => {
|
||||
const factory = (AppConfig) => {
|
||||
AppConfig.registeredOnlyTypes = AppConfig.availablePadTypes;
|
||||
AppConfig.disableAnonymousPadCreation = true;
|
||||
AppConfig.disableAnonymousStore = true;
|
||||
|
||||
return AppConfig;
|
||||
};
|
||||
|
||||
|
||||
if (typeof(module) !== 'undefined' && module.exports) {
|
||||
module.exports = factory(
|
||||
require('../www/common/application_config_internal.js')
|
||||
);
|
||||
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
|
||||
define(['/common/application_config_internal.js'], factory);
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
config.js: |
|
||||
module.exports = {
|
||||
httpUnsafeOrigin: 'https://${CRYPTPAD_HOST}',
|
||||
httpSafeOrigin: 'https://${CRYPTPAD_SANDBOX_HOST}',
|
||||
httpAddress: '0.0.0.0',
|
||||
//httpPort: 3000,
|
||||
//httpSafePort: 3001,
|
||||
// websocketPort: 3003,
|
||||
// maxWorkers: 4,
|
||||
//otpSessionExpiration: 7*24, // hours
|
||||
//enforceMFA: false,
|
||||
//logIP: false,
|
||||
adminKeys: [
|
||||
"[oleksandr@cryptpad.berezovskyi.dev/wiMN+oF1CaWHMaWxv11M+xqvnjXfNgshqoKIvEcG9Ss=]"
|
||||
],
|
||||
//inactiveTime: 90, // days
|
||||
//archiveRetentionTime: 15,
|
||||
//accountRetentionTime: 365,
|
||||
//disableIntegratedEviction: true,
|
||||
//maxUploadSize: 20 * 1024 * 1024,
|
||||
//premiumUploadSize: 100 * 1024 * 1024,
|
||||
filePath: './datastore/',
|
||||
archivePath: './data/archive',
|
||||
pinPath: './data/pins',
|
||||
taskPath: './data/tasks',
|
||||
blockPath: './block',
|
||||
blobPath: './blob',
|
||||
blobStagingPath: './data/blobstage',
|
||||
decreePath: './data/decrees',
|
||||
logPath: './data/logs',
|
||||
logToStdout: true,
|
||||
logLevel: 'verbose',
|
||||
logFeedback: false,
|
||||
verbose: true,
|
||||
installMethod: 'docker',
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
# Backs up user data (blob, datastore, data, block directories)
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: cryptpad-data-backup
|
||||
namespace: cryptpad
|
||||
labels:
|
||||
app: cryptpad-backup
|
||||
spec:
|
||||
schedule: "0 2 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cryptpad-backup
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: resticprofile
|
||||
image: creativeprojects/resticprofile:0.32.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
resticprofile -c /secrets/profiles.yaml -n cryptpad-data backup
|
||||
resticprofile -c /secrets/profiles.yaml -n cryptpad-data copy
|
||||
env:
|
||||
- name: B2_ACCOUNT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: cryptpad-backup-config
|
||||
key: B2_ACCOUNT_ID
|
||||
- name: B2_ACCOUNT_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: cryptpad-backup-config
|
||||
key: B2_ACCOUNT_KEY
|
||||
volumeMounts:
|
||||
- name: secrets
|
||||
mountPath: /secrets
|
||||
readOnly: true
|
||||
- name: data
|
||||
mountPath: /backup-data
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
memory: 1Gi
|
||||
volumes:
|
||||
- name: secrets
|
||||
secret:
|
||||
secretName: cryptpad-backup-config
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: cryptpad-data
|
||||
---
|
||||
# Backs up config and customization directories
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: cryptpad-config-backup
|
||||
namespace: cryptpad
|
||||
labels:
|
||||
app: cryptpad-backup
|
||||
spec:
|
||||
schedule: "0 3 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cryptpad-backup
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: resticprofile
|
||||
image: creativeprojects/resticprofile:0.32.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
resticprofile -c /secrets/profiles.yaml -n cryptpad-config backup
|
||||
resticprofile -c /secrets/profiles.yaml -n cryptpad-config copy
|
||||
env:
|
||||
- name: B2_ACCOUNT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: cryptpad-backup-config
|
||||
key: B2_ACCOUNT_ID
|
||||
- name: B2_ACCOUNT_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: cryptpad-backup-config
|
||||
key: B2_ACCOUNT_KEY
|
||||
volumeMounts:
|
||||
- name: secrets
|
||||
mountPath: /secrets
|
||||
readOnly: true
|
||||
- name: config
|
||||
mountPath: /backup-config
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
memory: 512Mi
|
||||
volumes:
|
||||
- name: secrets
|
||||
secret:
|
||||
secretName: cryptpad-backup-config
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: cryptpad-config
|
||||
@@ -0,0 +1,175 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: cryptpad
|
||||
namespace: cryptpad
|
||||
labels:
|
||||
app: cryptpad
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cryptpad
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cryptpad
|
||||
spec:
|
||||
securityContext:
|
||||
runAsUser: 4001
|
||||
runAsGroup: 4001
|
||||
runAsNonRoot: true
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
# # Create required subdirectories on NFS volumes before subPath mounts are used
|
||||
# - name: init-dirs
|
||||
# image: busybox:1.36
|
||||
# securityContext:
|
||||
# runAsUser: 0
|
||||
# runAsNonRoot: false
|
||||
# command:
|
||||
# - sh
|
||||
# - -c
|
||||
# - |
|
||||
# mkdir -p /data/blob /data/datastore /data/data /data/block /data/onlyoffice
|
||||
# mkdir -p /config/customize
|
||||
# chown -R 4001:4001 /data /config
|
||||
# volumeMounts:
|
||||
# - name: data
|
||||
# mountPath: /data
|
||||
# - name: config
|
||||
# mountPath: /config
|
||||
|
||||
# Clone the official CryptPad SSO plugin into an emptyDir shared with the main container
|
||||
- name: install-sso-plugin
|
||||
image: alpine/git:v2.47.2
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
runAsNonRoot: false
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
git clone --depth=1 https://github.com/cryptpad/sso /plugin
|
||||
chown -R 4001:4001 /plugin
|
||||
volumeMounts:
|
||||
- name: sso-plugin
|
||||
mountPath: /plugin
|
||||
|
||||
containers:
|
||||
- name: cryptpad
|
||||
image: cryptpad/cryptpad:version-2026.2.0
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
name: http
|
||||
protocol: TCP
|
||||
- containerPort: 3003
|
||||
name: api
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: CPAD_CONF
|
||||
value: "/cryptpad/config/config.js"
|
||||
- name: CPAD_MAIN_DOMAIN
|
||||
value: "https://${CRYPTPAD_HOST}"
|
||||
- name: CPAD_SANDBOX_DOMAIN
|
||||
value: "https://${CRYPTPAD_SANDBOX_HOST}"
|
||||
# Trust the Traefik pod CIDR so CryptPad sees real client IPs.
|
||||
# Adjust if your pod CIDR is different.
|
||||
- name: CPAD_TRUSTED_PROXY
|
||||
value: "10.0.0.0/8"
|
||||
- name: CPAD_REALIP_HEADER
|
||||
value: "X-Forwarded-For"
|
||||
- name: CPAD_REALIP_RECURSIVE
|
||||
value: "on"
|
||||
# Downloads and installs OnlyOffice frontend on first start (persisted via data PVC).
|
||||
# First startup will be slower while OnlyOffice assets are fetched.
|
||||
- name: CPAD_INSTALL_ONLYOFFICE
|
||||
value: "yes"
|
||||
volumeMounts:
|
||||
# User data — split into subdirs on a single NFS PVC
|
||||
- name: data
|
||||
mountPath: /cryptpad/blob
|
||||
subPath: blob
|
||||
- name: data
|
||||
mountPath: /cryptpad/datastore
|
||||
subPath: datastore
|
||||
- name: data
|
||||
mountPath: /cryptpad/data
|
||||
subPath: data
|
||||
- name: data
|
||||
mountPath: /cryptpad/block
|
||||
subPath: block
|
||||
# Customization (branding, themes) — persisted on NFS
|
||||
- name: config
|
||||
mountPath: /cryptpad/customize
|
||||
subPath: customize
|
||||
# OnlyOffice dist — local-path (not NFS): rdfind uses hard links which
|
||||
# require a real filesystem, and scanning NFS for dedup is very slow
|
||||
- name: onlyoffice
|
||||
mountPath: /cryptpad/www/common/onlyoffice/dist
|
||||
# SSO plugin — freshly cloned by init container on each pod start
|
||||
- name: sso-plugin
|
||||
mountPath: /cryptpad/lib/plugins/sso
|
||||
# sso.js mounted directly from the secret — overlays the NFS config dir
|
||||
# at this specific file path, no init container needed
|
||||
- name: sso-secret
|
||||
mountPath: /cryptpad/config/sso.js
|
||||
subPath: sso.js
|
||||
readOnly: true
|
||||
- name: application-config
|
||||
mountPath: /cryptpad/customize/application_config.js
|
||||
subPath: application_config.js
|
||||
readOnly: true
|
||||
- name: application-config
|
||||
mountPath: /cryptpad/config/config.js
|
||||
subPath: config.js
|
||||
readOnly: true
|
||||
# npm run build runs on every start and can take several minutes.
|
||||
# startupProbe absorbs that time; liveness/readiness take over once up.
|
||||
startupProbe:
|
||||
httpGet:
|
||||
port: 3000
|
||||
path: /
|
||||
failureThreshold: 120
|
||||
periodSeconds: 60
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 3000
|
||||
path: /
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 3000
|
||||
path: /
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
memory: 2Gi
|
||||
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: cryptpad-data
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: cryptpad-config
|
||||
- name: sso-plugin
|
||||
emptyDir: {}
|
||||
- name: onlyoffice
|
||||
persistentVolumeClaim:
|
||||
claimName: cryptpad-onlyoffice
|
||||
- name: sso-secret
|
||||
secret:
|
||||
secretName: cryptpad-credentials
|
||||
items:
|
||||
- key: sso.js
|
||||
path: sso.js
|
||||
- name: application-config
|
||||
configMap:
|
||||
name: cryptpad-config
|
||||
@@ -0,0 +1,24 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cryptpad
|
||||
namespace: cryptpad
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
# No Traefik auth middleware — authentication is handled natively via the OIDC SSO plugin
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- ${CRYPTPAD_HOST}
|
||||
secretName: cryptpad-tls
|
||||
rules:
|
||||
- host: ${CRYPTPAD_HOST}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: cryptpad
|
||||
port:
|
||||
number: 3000
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cryptpad-sandbox
|
||||
namespace: cryptpad
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
# The sandbox domain is required by CryptPad for CSP isolation of embedded content.
|
||||
# It must be a different origin from the main domain but points to the same backend.
|
||||
# No auth middleware — this domain serves sandboxed iframes with restrictive CSP headers.
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- ${CRYPTPAD_SANDBOX_HOST}
|
||||
secretName: cryptpad-sandbox-tls
|
||||
rules:
|
||||
- host: ${CRYPTPAD_SANDBOX_HOST}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: cryptpad
|
||||
port:
|
||||
number: 3000
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: cryptpad
|
||||
@@ -0,0 +1,56 @@
|
||||
# Default deny all ingress
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default-deny-ingress
|
||||
namespace: cryptpad
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
---
|
||||
# Allow Traefik to reach the CryptPad pod (both HTTP and API/WebSocket ports)
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-ingress-controller
|
||||
namespace: cryptpad
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: cryptpad
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: traefik
|
||||
ports:
|
||||
- port: 3000
|
||||
- port: 3003
|
||||
---
|
||||
# Allow backup pods egress to Synology (restic rest-server), B2, and DNS
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-backup-egress
|
||||
namespace: cryptpad
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: cryptpad-backup
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- ports:
|
||||
- port: 53
|
||||
protocol: UDP
|
||||
- port: 53
|
||||
protocol: TCP
|
||||
- ports:
|
||||
- port: 8000
|
||||
protocol: TCP
|
||||
- ports:
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: cryptpad-config-nfs
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: ""
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
mountOptions:
|
||||
- hard
|
||||
- noatime
|
||||
- timeo=30
|
||||
- retrans=3
|
||||
nfs:
|
||||
server: synology.storage.lviv
|
||||
path: ${CRYPTPAD_CONFIG_NFS_PATH}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: cryptpad-config
|
||||
namespace: cryptpad
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: ""
|
||||
volumeName: cryptpad-config-nfs
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: cryptpad-data-nfs
|
||||
spec:
|
||||
capacity:
|
||||
storage: 100Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: ""
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
mountOptions:
|
||||
- hard
|
||||
- noatime
|
||||
- timeo=30
|
||||
- retrans=3
|
||||
nfs:
|
||||
server: synology.storage.lviv
|
||||
path: ${CRYPTPAD_DATA_NFS_PATH}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: cryptpad-data
|
||||
namespace: cryptpad
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: ""
|
||||
volumeName: cryptpad-data-nfs
|
||||
resources:
|
||||
requests:
|
||||
storage: 100Gi
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: cryptpad-onlyoffice
|
||||
namespace: cryptpad
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: local-path
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cryptpad-backup-config
|
||||
namespace: cryptpad
|
||||
stringData:
|
||||
profiles.yaml: ENC[AES256_GCM,data:8t3Vx7GUX059eX6nxypFx/VacLL+hMy+muF3cybpSMNt5cQXONoUrFQnjW1VkWRgk/n/fqVa80HmT3g1JCeUBwNVDsM9DfH0VCiJxoMYrpDmYObkt82RUMnojhG6afPVPEduvaccOAjyslGnZAMAuQT+DIO/C8wayYDQJcIXJZeHorRJR/4P358EWw7h/p6ylNXGWPiI7IsIn0zsaePD3mshoA4ix+pUbBztTSk3Ij7iiGqltYIZkq8QYkOy4oiAmFTLxIlZfEfrMXrkOaHuvkGd1uftX3VfzJAaLmbGP5sEdkAPawYmGWLnU+ObFSL31zkbTCKhNtEwzQbDmw5RC4srVMj6UttDDs7LBzGZh/HUuXoKb+DIBlYV9siGwZH3bLrg7XoXVi2BimKPAQELlAtOfvOe7NZ8cBE34cCvd3MiZqN37uZYdx2sRHFPJp6PyWbglG2TIqj/J1lPKdZOMG7NpEvc2WXi8ltWjtNmDNjoLsaUh5+e3TqLIYkdd4BxwN0sEJFU/apyyGcS5/2TED0ZgBPINvdCVApTagNwIWjmi11VH2r5hxkb2OnZkwRjsyujmSexaFdS7jjBDXZNg6x1Q4vgBb70iCfTPqAGQyFkTpf3+AKopuPgto9LLztmmC7e4Q08D1ncDGatGRrgIXDhhhhFnbTKdle53gQKLWi8THxIF1/nlvu07zn+HYP5sWG38No2kpMJwCXeUoqXwHhcgVRlAyPka6ip+zWOyg/qdcJU4k4sCW4+DkrdwUHOCiT/0wYRMdeoDRJDLVS/5OESl3HboIQSTxDhgUid0iGH3zr35zwa3DR4mMXYPkh+Nodsp0oh5aG0lIDGrz+6lOVznYUTate1CVm3HN2zQPRyuTPMxILsEVC44qQ3KyR55VgZhwER2418wg68VFQA1uT8HTPEVZFPRgxdK4amIHr7cnC2B67/X0oeHJNbDEc0EAkEx8v08HklJQELNMNInRySW1UnBQOIWo3rl0PsZgyecWnK9vdwPjfEyzr8DVlfm7rGMNTWc7iIPwYom9jLHh4IgOU5TEVatwkd+zMS8ZeHPeFEdL/bf0YbdBqir8+X9ERSkG41OQGYL5Rhx3XM6oExfA4hGGqAEXfGTCAqIljkyKzo7280sIdkDvEBq+FMnCJ+APCSoPqNQUqZpEmWG/XP+Q+bgo5Pb32vlxL8dwcVmuiuu0cndW7gzA6YgfCRfTmGTNjW43J/h5ODBa8Vhu2WNiRMwCzH72ExOvwgKEBeP1LPHVcMLT0f/CMItcpzkuGwzlitZMAm2nmCdSB71hdtsKD6x5hIzhsDCAzZz6VCUF+NAgjOyks3urlDiBhyMnzetXnDgtLB1kL8qwIHOvWQoF5S50KrCbHTNJUhBWBJzRom7oZJOdyHwo7MmSMvRWTY5xA09umSia6/qyDlGeCHDYx/C8i3/WGUGsVKB2EzsfY=,iv:z79twnVkLyonYaOgz2f4ZW7noMlDxZgDRqYySBj+Mlk=,tag:2W0mtSBGiqgWPKDyZrKYRg==,type:str]
|
||||
restic-password-nas: ENC[AES256_GCM,data:0g81dj/gNwnBPkbVXR1o5mZ4axAhnL7YwSNcnE2fI1DWly4cO3kNyxta5OdgTAGZwNeiT+qaOFi6HXHn9fSHlQ==,iv:OZEza2gkiygZ1KzvD3MJZ/JXIrfbcZenKcOJfMlEjvM=,tag:rnJ8sFQgzD6g3pEHRQLg3A==,type:str]
|
||||
restic-password-b2: ENC[AES256_GCM,data:sazn2f09uZsFU1/MY30iPj0gx9pSFvcy/JgHsXTLMFbq5OimAfHsiYR1nMf2mQ8GwqMtQTF4t0lkfDnmy/8Qyg==,iv:HOI8oohNCCtP/heNBCQ5HEKfRVdMalv3MU/puRkOQ4A=,tag:aLKeUbwED2TjeAfkydj+MQ==,type:str]
|
||||
B2_ACCOUNT_ID: ENC[AES256_GCM,data:Jh4gWGGt5wVaVAMG5oWFXW7W0Taq9Qiyhw==,iv:XZtsiYUtFtSDhgLpG3uNIGLHWqGmCM3zHq2RUu6tdXM=,tag:KQfrBmkAYadVvhtgAzDNzw==,type:str]
|
||||
B2_ACCOUNT_KEY: ENC[AES256_GCM,data:XI+P//Ti512rQfDgmV00Bq3Wxly6//ESBKsDItXdug==,iv:/xAPGSJIathFIwyWUQoBW2DwYBpHnJCElnRMe16Pf9s=,tag:xSh2kFbQnw+bpAbbQ3rHWQ==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlVGM3NlRSenp4d05sTEdY
|
||||
c0JkdW5Xd2RQU3I4SlVTczVRWnVaRUY5RHdnClM1QVlGR3lRWGZCVXR0RjR1Kzlh
|
||||
ck4vLzNpeThIdmV4citIaFlVWnpFWXMKLS0tIHV2aWtSVTRxMzUrbldwQVRLRHE5
|
||||
ajAzS2pINWIvQTc0T0FhMit6ZHNVRlkKW6hYD2mjAtZRyC8/fbSWiij1/clTB4br
|
||||
sAmsCMBIDQf+r12TuzzpLiMnNOIU349GcYxuA24GWaEanordqCLz3A==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-03-13T12:31:48Z"
|
||||
mac: ENC[AES256_GCM,data:O+jD7jCBoQYNrRr+j/yn4YkGLaggHee8YVmvE5I5QXOB5pfPg09yQbuYj2IhKPBbVXl24ejlNDEAG3/qDaWgg1IlB+dKeTyrqxWq+mQ7vM9bgNRhf4ozS1lL3NR23V6JhilrosxTlFYCajerxdUbCh0vBXqsdJ0cIV8yggtQ7eY=,iv:gJkOvEFY4L/7qE3zMYnnon8XcGUegmcWKvKQ+/rxBVo=,tag:xQ1Qo7SgOs3N8451xX7Yxg==,type:str]
|
||||
encrypted_regex: ^(data|stringData|email)$
|
||||
version: 3.12.1
|
||||
@@ -0,0 +1,25 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: cryptpad-credentials
|
||||
namespace: cryptpad
|
||||
stringData:
|
||||
#ENC[AES256_GCM,data:s4VxDsDqwHw6ZhFNzA/mS+gdPwZlV5AcBDRUqfHTKSzjQPVg7unz7HkyfXtSBt36aH3P7fR9lUF1XtNvH9fF+6EfCa1dzIToSEw=,iv:TuYHrkjDFkA7fHhWzaHGrptyP5ClwiMArjoTf5LHXrQ=,tag:gvZxTmeeCEDTISgIpKxhhA==,type:comment]
|
||||
#ENC[AES256_GCM,data:CXpQCo8r5GCE6lIpXpXlab0CEdMCuNC54Ynh4ahfE1fv2c49wNoiQNRQAjONbmhUoUIiUcCehNmuw/ZK5Fd3rvVUhXdUAKYo,iv:U6vrAHUz22QWSLxBYRFea+LXajuHIh8NlggB6DAoCjc=,tag:nnV8RMrNv+trUQgXpCvkJA==,type:comment]
|
||||
#ENC[AES256_GCM,data:7TBFv3i2TzlyAcVhAdFE6T2F0E8pdAbYZorT6/brY8Ed+g6LvnwdVaYPcpJ7P6ETWXraqbhoktWICYklr35PAg==,iv:xsPinB1/gkzMHyzsrJ2gj8Fm0PWaIQbtBIB7fT1BTzI=,tag:O9dv+myneU7neE7JiRGuBQ==,type:comment]
|
||||
sso.js: ENC[AES256_GCM,data:u1XHMXQKf6QtqqjzNx8LZIEZxKSWhVFZbgvKBqsGoKo7kve0nZynH/bYohVO3XDBtahBqDCgeAD/rj5OE2MlEhwF0OTfylpPmlDjD3tuwogMv2NSDwERzzGv2BvMY97kIRwbGP3OtHGEEGO32JmNKWInZH7BrazeX5gvNLKjHk8XtCN8jKxoJwGEYw7EM8s219iFyQISKjo9/yElv4JVlecIa1bcbzswRsgWqzjG3kBJ2HLRcnH4Nr8D8VCDEdO32GHS0Y9Z7b3eaaj0YQyWleyAfqlufktMcr+SFDSQy4sPnzFuydi7kPXRb6OVDZLaKMVA4422VEEsuAOcz5CUZIJAg5KhPz9lZsruRw3hZBTjhRE2G05TwsvJNwY005rVe2pHL/Q3OZRKNZIfXK34lOwnBQMnBPJvtogIT+hZUBBAyz82nuR8KuujsGLo4sJPC/Vuayg9CrwxbQRx7fit5sdu5djwvVleBKyayhbBaP23BZToB9znSNm5HCOpohpAg4OYXazRpLBGH6lHla1tZy7MpfnoltEOHjeBQoqpez3zBQ9zNichIuPVnIK0h1dqAjLlZc5n7d9L6AHDrzSRq40iOnuc4ZuATMXtmd1JynSrjCH4FYKtROV9V3JXUCK9GwnZ04D26rbwxCKKykHkABF4AFDvv4ZeJPvN+Ag6R8TuSG74qzRUjvBP1+2uR/y9IsN5dGviYKzGnVFvkD87RrF3nZQc40+C4CfAuP9xTLJPzZ0b1t6Qr3Q52LipSyOt/I04ULToOU4U7heEkpbZxJAWiAlgLskjy9TfL/OY1uyZT9ZanWAhRVBa0dk2h8I5p9a01XtR8X3sIqg41i1K3VqEcKGk7IQMf+WmiUdLef7qx6Gq3+Di067NJ7fKKNAN3IFGE/LBlBVaj1fQNOaCfjSJ/A==,iv:FSSJUe6ymLHIM5CQLVgemHX71Z7Bz609WsNkOs9QM1E=,tag:Wxdnfa6WXyXVxI3VjU6dMA==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5Vm8xRmZ0WDJRMmNPVUxq
|
||||
T3VQSUVUYnVER0FFVW5FYWFHaUV3S2daWFJvCmxLdWpLVHpnMWNQaTlDZ0VOZ0JL
|
||||
K044cmpRZm50SmZNNWZlWjBDS1hGS2cKLS0tIGRxMnZTTWRRMlg5eVBOb3pVYUlK
|
||||
clRoWlp2SjQrOGFjQW92VnQ4amxUMXMKMjcBWo44X+Y2kmwpKZcBsBZZkWQJZEOf
|
||||
VscZUj8VAZKF9IxOmbBaFc2EBi5xI7sYJSJ+gaLHHKLhcSNu59P+kQ==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-03-13T19:48:58Z"
|
||||
mac: ENC[AES256_GCM,data:0mpN6sWiCHp3zVIKIdIEAnBEcczPb1YVfj4fae50JnJ4jF9ZZ6Wz4sQny00LKN59NFFHHM5bOa+ibqEXZ3pgtF0WQmmc5Sfn5w6n66L4mSJ0Pjx4akMJYjt5xMkRuLG996yZXg7pvSd3YrMgJEqf0hiexN+KJMLDqUwDIYpUHpc=,iv:BMIyWxc0k8SZTxS9/F3Vxfa4HY/WordDRQTfJmZZSI0=,tag:FpPT98LvKY2HgeBfZOw5TQ==,type:str]
|
||||
encrypted_regex: ^(data|stringData|email)$
|
||||
version: 3.12.1
|
||||
@@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: cryptpad
|
||||
namespace: cryptpad
|
||||
spec:
|
||||
selector:
|
||||
app: cryptpad
|
||||
# Session affinity ensures WebSocket connections always reach the same pod
|
||||
sessionAffinity: ClientIP
|
||||
ports:
|
||||
- port: 3000
|
||||
targetPort: 3000
|
||||
name: http
|
||||
- port: 3003
|
||||
targetPort: 3003
|
||||
name: api
|
||||
@@ -77,7 +77,7 @@ spec:
|
||||
|
||||
image:
|
||||
# renovate: datasource=docker depName=freikin/dawarich
|
||||
tag: "1.21.0"
|
||||
tag: "1.20.0"
|
||||
|
||||
keyBase:
|
||||
existingSecret: dawarich-credentials # key: value
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: firefly-db-backup
|
||||
namespace: firefly
|
||||
labels:
|
||||
app: firefly-backup
|
||||
spec:
|
||||
schedule: "0 2 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: firefly-backup
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
initContainers:
|
||||
- name: pg-dump
|
||||
image: postgres:17
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: PGHOST
|
||||
value: firefly-db
|
||||
- name: PGUSER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-credentials
|
||||
key: DB_USERNAME
|
||||
- name: PGPASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-credentials
|
||||
key: DB_PASSWORD
|
||||
- name: PGDATABASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-credentials
|
||||
key: DB_DATABASE
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- pg_dump --clean --if-exists > /backup/dump.sql
|
||||
volumeMounts:
|
||||
- name: backup-tmp
|
||||
mountPath: /backup
|
||||
containers:
|
||||
- name: resticprofile
|
||||
image: creativeprojects/resticprofile:0.32.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
resticprofile -c /secrets/profiles.yaml -n firefly-db backup
|
||||
resticprofile -c /secrets/profiles.yaml -n firefly-db copy
|
||||
env:
|
||||
- name: B2_ACCOUNT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-backup-config
|
||||
key: B2_ACCOUNT_ID
|
||||
- name: B2_ACCOUNT_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-backup-config
|
||||
key: B2_ACCOUNT_KEY
|
||||
volumeMounts:
|
||||
- name: secrets
|
||||
mountPath: /secrets
|
||||
readOnly: true
|
||||
- name: backup-tmp
|
||||
mountPath: /backup
|
||||
volumes:
|
||||
- name: secrets
|
||||
secret:
|
||||
secretName: firefly-backup-config
|
||||
- name: backup-tmp
|
||||
emptyDir: {}
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: firefly-uploads-backup
|
||||
namespace: firefly
|
||||
labels:
|
||||
app: firefly-backup
|
||||
spec:
|
||||
schedule: "0 3 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: firefly-backup
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: resticprofile
|
||||
image: creativeprojects/resticprofile:0.32.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
resticprofile -c /secrets/profiles.yaml -n firefly-uploads backup
|
||||
resticprofile -c /secrets/profiles.yaml -n firefly-uploads copy
|
||||
env:
|
||||
- name: B2_ACCOUNT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-backup-config
|
||||
key: B2_ACCOUNT_ID
|
||||
- name: B2_ACCOUNT_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-backup-config
|
||||
key: B2_ACCOUNT_KEY
|
||||
volumeMounts:
|
||||
- name: secrets
|
||||
mountPath: /secrets
|
||||
readOnly: true
|
||||
- name: uploads
|
||||
mountPath: /uploads
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
memory: 1Gi
|
||||
volumes:
|
||||
- name: secrets
|
||||
secret:
|
||||
secretName: firefly-backup-config
|
||||
- name: uploads
|
||||
persistentVolumeClaim:
|
||||
claimName: firefly-firefly-iii
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: firefly
|
||||
@@ -0,0 +1,95 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default-deny-ingress
|
||||
namespace: firefly
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-ingress-controller
|
||||
namespace: firefly
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: firefly-iii
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: traefik
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-app-to-db
|
||||
namespace: firefly
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: firefly-db
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: firefly-iii
|
||||
ports:
|
||||
- port: 5432
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-backup-to-db
|
||||
namespace: firefly
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: firefly-db
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: firefly-backup
|
||||
ports:
|
||||
- port: 5432
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-backup-egress
|
||||
namespace: firefly
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: firefly-backup
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- ports:
|
||||
- port: 53
|
||||
protocol: UDP
|
||||
- port: 53
|
||||
protocol: TCP
|
||||
- ports:
|
||||
- port: 8888
|
||||
protocol: TCP
|
||||
- ports:
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
- ports:
|
||||
- port: 5432
|
||||
protocol: TCP
|
||||
to:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: firefly-db
|
||||
@@ -0,0 +1,83 @@
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: firefly-iii
|
||||
namespace: flux-system
|
||||
spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: firefly-iii
|
||||
version: 1.9.13
|
||||
reconcileStrategy: ChartVersion
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: firefly-iii
|
||||
namespace: flux-system
|
||||
targetNamespace: firefly
|
||||
interval: 1m0s
|
||||
install:
|
||||
remediation:
|
||||
retries: 3
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: 3
|
||||
values:
|
||||
deploymentStrategyType: Recreate
|
||||
|
||||
image:
|
||||
# renovate: datasource=docker depName=fireflyiii/core
|
||||
tag: version-6.7.9
|
||||
|
||||
podSecurityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
memory: 512Mi
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
storageClassName: nfs-synology-ssd
|
||||
storage: 5Gi
|
||||
|
||||
config:
|
||||
existingSecret: firefly-credentials
|
||||
env:
|
||||
DB_HOST: firefly-db
|
||||
DB_CONNECTION: pgsql
|
||||
DB_PORT: "5432"
|
||||
DB_DATABASE: firefly
|
||||
DB_USERNAME: firefly
|
||||
TZ: Europe/Kyiv
|
||||
TRUSTED_PROXIES: "10.244.0.0/24"
|
||||
APP_URL: https://${FIREFLY_HOST}
|
||||
AUTHENTICATION_GUARD: remote_user_guard
|
||||
AUTHENTICATION_GUARD_HEADER: Remote-Email
|
||||
AUTHENTICATION_GUARD_EMAIL: Remote-Email
|
||||
MAIL_MAILER: smtp
|
||||
MAIL_HOST: smtp.protonmail.ch
|
||||
MAIL_PORT: "587"
|
||||
MAIL_ENCRYPTION: tls
|
||||
|
||||
cronjob:
|
||||
enabled: true
|
||||
auth:
|
||||
existingSecret: firefly-credentials
|
||||
secretKey: STATIC_CRON_TOKEN
|
||||
schedule: "0 3 * * *"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
traefik.ingress.kubernetes.io/router.middlewares: authelia-chain-authelia-authelia-auth@kubernetescrd
|
||||
hosts:
|
||||
- ${FIREFLY_HOST}
|
||||
tls:
|
||||
- secretName: firefly-tls
|
||||
hosts:
|
||||
- ${FIREFLY_HOST}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"kubernetes": {
|
||||
"fileMatch": ["kubernetes/app/firefly/.+\\.yaml$"]
|
||||
},
|
||||
"customManagers": [
|
||||
{
|
||||
"customType": "regex",
|
||||
"fileMatch": ["kubernetes/app/firefly/release\\.yaml$"],
|
||||
"matchStrings": [
|
||||
"#\\s*renovate:\\s*datasource=(?<datasource>\\S+)\\s+depName=(?<depName>\\S+)\\s*\\n\\s*tag:\\s*[\"']?(?<currentValue>[^\\s\"']+)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchFileNames": ["kubernetes/app/firefly/**"],
|
||||
"semanticCommitScope": "k8s/firefly"
|
||||
},
|
||||
{
|
||||
"description": "Tags are version-X.Y.Z; skip floating version-X.Y, version-X, latest, develop",
|
||||
"matchFileNames": ["kubernetes/app/firefly/**"],
|
||||
"matchPackageNames": ["fireflyiii/core"],
|
||||
"versioning": "regex:^version-(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)$"
|
||||
},
|
||||
{
|
||||
"matchFileNames": ["kubernetes/app/firefly/**"],
|
||||
"matchPackageNames": ["fireflyiii/core"],
|
||||
"matchUpdateTypes": ["patch", "minor"],
|
||||
"automerge": true
|
||||
},
|
||||
{
|
||||
"matchFileNames": ["kubernetes/app/firefly/**"],
|
||||
"matchPackageNames": ["fireflyiii/core"],
|
||||
"matchUpdateTypes": ["major"],
|
||||
"automerge": false
|
||||
},
|
||||
{
|
||||
"description": "Ignore infrastructure images",
|
||||
"matchFileNames": ["kubernetes/app/firefly/**"],
|
||||
"matchPackageNames": ["postgres", "creativeprojects/resticprofile"],
|
||||
"enabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: firefly-iii
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m0s
|
||||
url: https://firefly-iii.github.io/kubernetes
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: firefly-backup-config
|
||||
namespace: firefly
|
||||
stringData:
|
||||
profiles.yaml: ENC[AES256_GCM,data:2IDoUurD5tw5uTUX/rWuhGZbaCcYcR/J2ClSdr61rq5w1jjZunnyqeY8Vrp2LSaiDhYd6nIHB0oeM8HVsvkzbLksXVzbS/4/098KEJtey3I7yxfvvlgu9ZKbw0wuLqeXjfEkl7cArTdjMKwA3/B5/Jmqw9Q0g5QUPP97MF12Hagg/kpKipXD/n5YoOtv7ZqfHnxrIIh6XXIpa98pJ58x+7ct7K7DeUUj1s/3WAI7idJ4kTexwOb/NVup9qpheuxqAZmF0z8YUC73W0qzQzgUAOJ4+O09xc15PFRq19a95W9SnSHn8Ao56S67s26d2Hbp4JCa8U6MxHDJ09IdHzpu/bmGAVL6LxDheegYOLZWJtNoAryyE6sQN2PJQxIwFngKaMvs6CHSrZWIbCfKzIhNrJPr/u/l5pDvCSMcvg2oX7Bkc2uJ4WY/yFXSHhaWIdJv0VSvcUH04li1inrZe9AYfnsajRVuzgXrnhonqy/2NXrH32leSVjEeU97+Ma1eOX/t3+SNBglL5kJxJIRJ/tSkPDe/9GpVr+2QNQUmRMuIpSsRNQgGODU/XCFrMyJ1Ikl+pKhHitOELfckJ3FuZPRJB6HEqfkE2fZLK5e2G0JRJ4FinYFo6HkXzk4IAzLDlN2E7kgyPyCqHJQt4C/X+OTwE6MErPEPgE5o9pwC8TGv+Q8AN6rRdYs7FPBR9jxqWxoSTCdz49sVf/kcgcWnicn8XmFEGdWTWk3MqKDe8YdN6fyqPha2lxpsz7FkZMcTV2ZrLvIR65lkmL9Vb1I7SwroYuTr+sj3jTAJLZXgRTXpKodc2zTQR9rQEKvA1C7YgchTJa4axDkMY37MDHZnHPW6MEmPlpPw8NMge6QfNYDTAWvrwThGWQ051RSm8KbYfm+qlMWqx+zCFRcypKwBUKVWuf7TYa0jnL9u4QrUvpLBAr6jErNRdRLgm79h80n/ucPEBLCHlzhPzHGW/evdWMDTCd0B5dYbP0zpPkGEmzfBfnH23ru48DotzPV/y0j+SNacgUXQPFkC5x50jOS2Fay+5qInOyA75aSiFD5EMZmf8gx1a3+rDtUt+6Qict8Umtb3ZzpTrWxvxbV5oxCXNwWShSLAFyfwoNE1sEq++xu+Jp0SzTuSg+Kxz+CmuCJ4bnbYkrhJxyKfw==,iv:WdIFJmhmZ8S+ma5l5SS7de2PrxZJgKK2njrPbLVQSuo=,tag:/mPn6QHatxQYwsZInrK30w==,type:str]
|
||||
restic-password-nas: ENC[AES256_GCM,data:RIn57MOwyqVGR/GuVpIXi+p5jZ2UQm4VgioCgE1GXIk=,iv:DcF6cD+AVa0LoXY6+wACfQvoAtdndl0chmPcm463fEE=,tag:RK9FF1RMFsfHGmg+ZcBEVA==,type:str]
|
||||
restic-password-b2: ENC[AES256_GCM,data:nIcy6uJY4fCjQH3aVggt3meckNAytAsw/ZNnGBBm7DM=,iv:zPf9YTDgT9dlq1nlyW6weMjZ0b3eVAuB15Lyx897xGU=,tag:+x0Sn3UQZEyhdMYrTK+Nqg==,type:str]
|
||||
B2_ACCOUNT_ID: ENC[AES256_GCM,data:t4YqtZRIpLpP7QHGBTeZ8vCL2L+1BjbLug==,iv:TvsBorT7r5A1d1AjPbYZ2vnm0x7gCPe4qY2bXFoK66Y=,tag:zaRhrGV/4siiWiykPlF6Gw==,type:str]
|
||||
B2_ACCOUNT_KEY: ENC[AES256_GCM,data:xZjFBj7oT2FcKHdwGBzyD1xR/bKtHGLEBsnAqblE+g==,iv:gtJfZvTZucbx6983OOk7WdD8KhJtrW6Wv0hnECYXQ8s=,tag:DCvHrL1Wv3NodzbH5aXylQ==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRSVBheDRtOEd0T0RJeG5x
|
||||
dnRtN2RTeEs1andrVk9XUTZtMklDdmx5aEhrCmlNR1puUHVGaFYrUHNHYU1hbnpz
|
||||
Q2J2ditrY1FuTXg3MUsvWm05WFcrUVUKLS0tIHdqeGFvNnkvVkdPWUphSkhETUpi
|
||||
R1RWWEpDVkdHamx3M1p5a2YwUkR2REEKp/sZFcCNeZnHCvpwcOu827HLg0wxLWJG
|
||||
EXHzgm5lZgiA/pQgxOJCiaz9HoNn3ttVE8z3aCnShUqAoXzpnh1Rpw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-04-21T20:22:40Z"
|
||||
mac: ENC[AES256_GCM,data:wlXAnMwySlqHadgeAtgC+QCceqSw+mODWzKrqOTb4JSqvK1aEbPDUJWn8ow+0ff5OcU/3XVBr6b9GVzD6DsPXhg/aLsM/d5h6BRFb/qmrusD5YRyScu/gokwiD52I3qDf7rHeLNsYmoO4I6KuwEQRXasJ6H+QIOTNUxbcd+TTnM=,iv:8dNbZg7oJODgdR7mT3XgKL7sXooNYAvr8GM2l0DK6ao=,tag:M2JPh6qkisTG+A+2DCgABA==,type:str]
|
||||
encrypted_regex: ^(data|stringData|email)$
|
||||
version: 3.12.2
|
||||
@@ -0,0 +1,30 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: firefly-credentials
|
||||
namespace: firefly
|
||||
stringData:
|
||||
APP_KEY: ENC[AES256_GCM,data:OugBKeVQqUDf8rFrGxMYgNROPesgVLsoKuQmWVvW9Kg=,iv:hnASzIOuoLczCUf0Luq2d6YXz5sKoHHawSys29cCMaE=,tag:u3SeTEc0VIb/NOwnXTn9Vg==,type:str]
|
||||
STATIC_CRON_TOKEN: ENC[AES256_GCM,data:YL+ghixRjb1t6K1Q09clyxlYeYSj3rsy82n78efFVAw=,iv:V2q7uKOpl3e/FROZFvH7YCXBSUnQm7QcwqFyZaBbx2I=,tag:dZ1XPxEOGgAvfyL426rDTw==,type:str]
|
||||
DB_USERNAME: ENC[AES256_GCM,data:Vk4jFf4Znw==,iv:LpP9DqkC1IlAwFNak8MTLPLf75iEpDwEm9ReLZPxRJo=,tag:qMexrfvQONJjfG11xh07wA==,type:str]
|
||||
DB_PASSWORD: ENC[AES256_GCM,data:SsGb3qXMiq7EqGyfU2E6xqku1HbmWM8C,iv:+3V3Fh76gim9wcrhYd6CX7smyz0Kc8Qywx5j0wrmVaw=,tag:UawliAt7zOD0rpxNe3KcPw==,type:str]
|
||||
DB_DATABASE: ENC[AES256_GCM,data:tLd4MWLoqQ==,iv:sG65BMBOAaKqYgCZ/xpv7LYaDNAPX+9KAeOBvYfUbgw=,tag:tRKptDmGV3iwV6luVFv/wQ==,type:str]
|
||||
MAIL_USERNAME: ENC[AES256_GCM,data:npTpeFJuH7rOmPN7+0tuAgSdK+OxIjHIOHFVhFGcwA==,iv:ugGlT9MfWedvLFb/iQTxSIB5pPQsKGS5utalEP0pyUs=,tag:iv1i73xuCs0svFIgysb9dA==,type:str]
|
||||
SITE_OWNER: ENC[AES256_GCM,data:WhpJSSWjniUumYjtRLHbOc51tcZ84JBtWw==,iv:nfy/1lXXlQYQW49LQevRevbVee1DODvqFKLPm3nRmU0=,tag:3nMwbS06dxD8oTvURw3OWA==,type:str]
|
||||
MAIL_FROM: ENC[AES256_GCM,data:JcZt/4D8pa6St+3Z5oX61Ez7I0PscCIDSJhVnDUyfQ==,iv:r8rSDEXjdWzpivA3QQQRiQScdv6rNK5VzAnlwl+13jQ=,tag:N55MPwZ6KGO9L18DTOJ/CA==,type:str]
|
||||
MAIL_PASSWORD: ENC[AES256_GCM,data:YrqECSPI6I7aYdx+oUmgGA==,iv:rvggM+Chw0JIOqfifzH5Q5yw4JhAv5BZgJfKevfWm2c=,tag:c9Wmxy8iivL4DKwEQj8VPg==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWdDRxUU1walNTNWY1VlVj
|
||||
MVB3OFo2Nm03b3haTjFtV09TaWtuYnJXNEN3CmFFOUM4czgxWGFubUJrNDRpOTBq
|
||||
eHB0ckJNZnltMC84S2NyQVRmUWNNMjQKLS0tIERUL0hwSzVmYlRHSVJwVnE0bkVt
|
||||
Zm1kOVhMU0JOa1U1MzN2M2FhdlVYUG8KhOhnu/8FxuEJdW5O0HeYVCu5eLgeyqaN
|
||||
Q88TjwbcwcIruXo/e0ATxWRp+yzwGB0nspQHZzAMP16uN6r3gZo3Zw==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-04-22T21:18:05Z"
|
||||
mac: ENC[AES256_GCM,data:JszWTjBGpBSuldSp+k/ZhO9P6+v+6WhTddv56WSHkkh1JT0JnomgKYReMj5bDxYeA6hJPLgFmD0N4gjporu3n8oCvADJgpXnVTEmYPnIJJqhEn3z8pGCKjeuxEvJk1MV7XvLEhZZJuvvb04uquyxvl1M8h9BJSCuPk7wxcM5pLA=,iv:MlF48Pqoi4239h3NAFj0VMO6YXJSt88GqCT+cA0qqXY=,tag:/mgkPgXMX3iyTomvKikO8A==,type:str]
|
||||
encrypted_regex: ^(data|stringData|email)$
|
||||
version: 3.12.2
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: firefly-db
|
||||
namespace: firefly
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector:
|
||||
app: firefly-db
|
||||
ports:
|
||||
- port: 5432
|
||||
targetPort: 5432
|
||||
@@ -0,0 +1,80 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: firefly-db
|
||||
namespace: firefly
|
||||
labels:
|
||||
app: firefly-db
|
||||
spec:
|
||||
serviceName: firefly-db
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: firefly-db
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: firefly-db
|
||||
spec:
|
||||
securityContext:
|
||||
runAsUser: 999
|
||||
runAsGroup: 999
|
||||
fsGroup: 999
|
||||
containers:
|
||||
- name: postgres
|
||||
image: postgres:17
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-credentials
|
||||
key: DB_USERNAME
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-credentials
|
||||
key: DB_PASSWORD
|
||||
- name: POSTGRES_DB
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: firefly-credentials
|
||||
key: DB_DATABASE
|
||||
- name: PGDATA
|
||||
value: /var/lib/postgresql/data/pgdata
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
startupProbe:
|
||||
tcpSocket:
|
||||
port: 5432
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
failureThreshold: 30
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 5432
|
||||
periodSeconds: 30
|
||||
failureThreshold: 5
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 5432
|
||||
periodSeconds: 10
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
memory: 1Gi
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
storageClassName: nfs-synology-ssd
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
@@ -199,11 +199,11 @@ spec:
|
||||
type: RuntimeDefault
|
||||
|
||||
image:
|
||||
# renovate: currentValue=1.27.3-rootless currentDigest=sha256:1c17ecaead42eb3b5391553d8708103a4beb0e86edf5b9ebc1eb269c318845f2
|
||||
# renovate: currentValue=28.3.0-rootless currentDigest=sha256:40c5a68a0463c0940e1ae32626c8300dd0572f3558e390073ad8379ca67c0e1d
|
||||
|
||||
tag: 1.27.3
|
||||
tag: 28.3.0
|
||||
|
||||
digest: sha256:1c17ecaead42eb3b5391553d8708103a4beb0e86edf5b9ebc1eb269c318845f2
|
||||
digest: sha256:40c5a68a0463c0940e1ae32626c8300dd0572f3558e390073ad8379ca67c0e1d
|
||||
rootless: true
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: grocy-config-backup
|
||||
namespace: grocy
|
||||
labels:
|
||||
app: grocy-backup
|
||||
spec:
|
||||
schedule: "0 2 * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: grocy-backup
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
initContainers:
|
||||
- name: sqlite-backup
|
||||
image: alpine:3.21
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
apk add --no-cache sqlite
|
||||
sqlite3 "file:/db-local/grocy.db?mode=ro" ".backup /backup/grocy.db"
|
||||
[ "$(sqlite3 /backup/grocy.db 'PRAGMA integrity_check')" = ok ]
|
||||
volumeMounts:
|
||||
- name: db
|
||||
mountPath: /db-local
|
||||
readOnly: true
|
||||
- name: backup-tmp
|
||||
mountPath: /backup
|
||||
containers:
|
||||
- name: resticprofile
|
||||
image: creativeprojects/resticprofile:0.32.0
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
resticprofile -c /secrets/profiles.yaml -n grocy-config backup
|
||||
resticprofile -c /secrets/profiles.yaml -n grocy-config copy
|
||||
env:
|
||||
- name: B2_ACCOUNT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: grocy-backup-config
|
||||
key: B2_ACCOUNT_ID
|
||||
- name: B2_ACCOUNT_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: grocy-backup-config
|
||||
key: B2_ACCOUNT_KEY
|
||||
volumeMounts:
|
||||
- name: secrets
|
||||
mountPath: /secrets
|
||||
readOnly: true
|
||||
- name: config
|
||||
mountPath: /config
|
||||
readOnly: true
|
||||
- name: backup-tmp
|
||||
mountPath: /backup
|
||||
volumes:
|
||||
- name: secrets
|
||||
secret:
|
||||
secretName: grocy-backup-config
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: grocy-config
|
||||
- name: db
|
||||
persistentVolumeClaim:
|
||||
claimName: grocy-db
|
||||
- name: backup-tmp
|
||||
emptyDir: {}
|
||||
@@ -0,0 +1,96 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: grocy
|
||||
namespace: grocy
|
||||
labels:
|
||||
app: grocy
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: grocy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: grocy
|
||||
spec:
|
||||
securityContext:
|
||||
# runAsNonRoot omitted — LinuxServer image requires root for s6-overlay init
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: migrate-db-to-local
|
||||
image: busybox:1.36
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
if [ ! -d /config/data ]; then
|
||||
echo "No /config/data yet — skipping"
|
||||
exit 0
|
||||
fi
|
||||
cd /config/data
|
||||
for db in *.db; do
|
||||
[ -f "$db" ] && [ ! -L "$db" ] || continue
|
||||
echo "Migrating $db to local storage..."
|
||||
cp "$db" "/db-local/$db"
|
||||
rm -f "$db"
|
||||
ln -sf "/db-local/$db" "$db"
|
||||
done
|
||||
echo "Done"
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
- name: db
|
||||
mountPath: /db-local
|
||||
containers:
|
||||
- name: grocy
|
||||
image: linuxserver/grocy:4.6.0
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: PUID
|
||||
value: "1027"
|
||||
- name: PGID
|
||||
value: "100"
|
||||
- name: TZ
|
||||
value: Etc/UTC
|
||||
- name: UMASK
|
||||
value: "002"
|
||||
- name: GROCY_AUTH_CLASS
|
||||
value: "Grocy\\Middleware\\ReverseProxyAuthMiddleware"
|
||||
- name: GROCY_REVERSE_PROXY_AUTH_HEADER
|
||||
value: Remote-User
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /config
|
||||
- name: db
|
||||
mountPath: /db-local
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 90
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
memory: 512Mi
|
||||
volumes:
|
||||
- name: config
|
||||
persistentVolumeClaim:
|
||||
claimName: grocy-config
|
||||
- name: db
|
||||
persistentVolumeClaim:
|
||||
claimName: grocy-db
|
||||
@@ -0,0 +1,24 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: grocy
|
||||
namespace: grocy
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
traefik.ingress.kubernetes.io/router.middlewares: authelia-chain-authelia-authelia-auth@kubernetescrd
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- ${GROCY_HOST}
|
||||
secretName: grocy-tls
|
||||
rules:
|
||||
- host: ${GROCY_HOST}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: grocy
|
||||
port:
|
||||
number: 80
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: grocy
|
||||
@@ -0,0 +1,50 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default-deny-ingress
|
||||
namespace: grocy
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-ingress-controller
|
||||
namespace: grocy
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: grocy
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: traefik
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-backup-egress
|
||||
namespace: grocy
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: grocy-backup
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- ports:
|
||||
- port: 53
|
||||
protocol: UDP
|
||||
- port: 53
|
||||
protocol: TCP
|
||||
- ports:
|
||||
- port: 8888
|
||||
protocol: TCP
|
||||
- ports:
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: grocy-config-nfs
|
||||
spec:
|
||||
capacity:
|
||||
storage: 5Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: ""
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
mountOptions:
|
||||
- hard
|
||||
- noatime
|
||||
- timeo=30
|
||||
- retrans=3
|
||||
nfs:
|
||||
server: synology.storage.lviv
|
||||
path: ${GROCY_NFS_PATH}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: grocy-config
|
||||
namespace: grocy
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: ""
|
||||
volumeName: grocy-config-nfs
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: grocy-db
|
||||
namespace: grocy
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: local-path
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: grocy-backup-config
|
||||
namespace: grocy
|
||||
stringData:
|
||||
profiles.yaml: ENC[AES256_GCM,data:oCTBX2/3P6xmhgi4ObZ81gl8goRVbqn1kL0ZNbiyurzX+omqsk7RrpVpKXsBwAXbqDIvwAk6gWyFQwT3g8LQEzpLJTXLSJE4pNSlpK3V02xtWzQu+28rPptnpRKqYqLM7DGeTmm1AJwzTNaCG7X46RBCc1gWP8S6vXvjjwpdqpsJH3G2zQpZsx8fmlRC3Vc38DWq1EQatnOi+RFMd2e7WufL6Yari9r14E8tpp3s1CZ1qwQ9W9K5uHAjXI+kuPL/NVRqYzkqwuSvmD4QTtHhH1eyu3WFIkIkah1tDYAivy+wCZjMLSe+q6heHiXIMV5IaVj0tTg/L5wbjwwQZXB53nnbuadViPuC3kFBej7QHkx6hwIMmEgKsp2skG7mFFLkYm9AXDOUxUZlrVU3eN47PxR2nFNYiWoQi5YSYkltBA4eEFZ9d4EKFcxWqj1tr7Y4VDtTjz2ZEfff/eyu8oJ75XdFb74Or8PXBKfFUpLjtOnax7Dc7Ff/fv1oTCvmHSxyv4pgdCfrkwERnMd5DspkxEppCqPpxtxMVIp31cAWDkvJ21wmCR9esCVHfe1eTj4+76Ok7c3vEXy49M/K9GstwPIWcEwiDrvftiXMEgHn95YRcNBGdjh3nLJ76DTO+pTsuVoOW5Bogcn4W7hGrgYKFFFESMzXUWRlF15YPN0RHg/2TI0I9Ke1lIOhUZj1ljefH9UwnERhKmXZ7AlsXvDPqUpYPwPmW55bbkDDa/VDp58BLA==,iv:tfWTMYk99Jkje9GAHLEUu77LIIP3XQjWaR25HQYHOLg=,tag:chdgdkDNp57f3OCIEOcYTw==,type:str]
|
||||
restic-password-nas: ENC[AES256_GCM,data:bG0LFnoo84t/r+cOGwjBPALc4dzrUj3Y9sMIuF7W1bjEkC9XsvSTQkczK1vmevhKuWdaaPYBr72gFzHcga7hUQ==,iv:tD1zf/mWknbMWtn/k0o8zJPcNvBK2Xa12eNSPyarybI=,tag:b03st6HrnpgXgkjDn0Gp+Q==,type:str]
|
||||
restic-password-b2: ENC[AES256_GCM,data:nyDMggQFE5Z2aCz/+kTdIIYe2BXisTb/MlVRfiRMCKjPPZDQ49jyf3z986jEXp9CyxKK665ySjnZEXn3txi/PQ==,iv:xvdCBKhlFPMH4cTXSm2VtFlpJlgbiggvdvfyjB7b76I=,tag:HEoizYHWcnkVTAe3APWsMA==,type:str]
|
||||
B2_ACCOUNT_ID: ENC[AES256_GCM,data:iXPdIOHzrggnh2pjimgFpQPNmA2FZgj8Mg==,iv:uS91Eg9xOHdZH113zfzlldgmCnFTopuOx/mmlsQsUC8=,tag:QDtAIhKwUkpt3v/tRZBYFA==,type:str]
|
||||
B2_ACCOUNT_KEY: ENC[AES256_GCM,data:RLT5JBRs7JfcJzFv6MilqXUTf5MocOeB1Pp1kQaUcg==,iv:FqyfAgE0wM7KFdPTBM2s3eKrXvzeUCO7qLyUwZUCPqM=,tag:rM15UoD2CyZ4yRYAQJ6xoA==,type:str]
|
||||
sops:
|
||||
age:
|
||||
- recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc
|
||||
enc: |
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArR1BFRmFZU3dJblVEVnRr
|
||||
bksyUzVHU0NjUkY2bGlXMzh3N0VEall4TFNVCmlBOUM4UnBPeVRPOHp3SnI5djUr
|
||||
elpodVdqSWt2L0ovMFZCNUZTVkxIcVEKLS0tIDRvNzdSQ3hxN0o3R3ViSzJoQi9E
|
||||
eDZTN3Fzak81RDJPR2dVOHlKOHJURzAK8DUPG6iCb6s7CQ6/+TZrIcLDKbN8bmh6
|
||||
lUwBN0Ciu8OAX+tRW6P3R/iZebc9s5jH/VkzGsR9qGjKw5BsNB811Q==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
lastmodified: "2026-03-11T19:28:51Z"
|
||||
mac: ENC[AES256_GCM,data:2SpDKL7Mwu/h7KLQoBXnk9iB84XNaex8hVbaUqmq4myIHAzvntb/zLXOexhsKPbQ0Ea7QDOKZIpkAG2dBPtRMtZWTr9uPpMoHzQG2lDGnYKGvBJXW/7WmaCdXlDf1FukqnYJZ3YoY+5xpTpYpXbWv1fyygp1YWmjKzY5qfcjtvw=,iv:TNbbiRmRRvPuQKiBjan7CVylNrt+eTjXxwKTJbeOhLw=,tag:9bqc9L4LlSq/d+oT257rsQ==,type:str]
|
||||
encrypted_regex: ^(data|stringData|email)$
|
||||
version: 3.12.1
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: grocy
|
||||
namespace: grocy
|
||||
spec:
|
||||
selector:
|
||||
app: grocy
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
name: http
|
||||
@@ -87,6 +87,10 @@ data:
|
||||
icon: paperless-ngx.png
|
||||
href: https://${PAPERLESS_HOST}/
|
||||
description: Documents storage
|
||||
- Grocy:
|
||||
icon: grocy.png
|
||||
href: https://${GROCY_HOST}/
|
||||
description: Chores and inventory tracker
|
||||
- Vikunja:
|
||||
icon: vikunja.png
|
||||
href: https://${VIKUNJA_HOST}/
|
||||
|
||||
@@ -28,7 +28,7 @@ spec:
|
||||
main:
|
||||
image:
|
||||
repository: ghcr.io/immich-app/immich-server
|
||||
tag: v3.3.6
|
||||
tag: v3.3.5
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: tile-cache
|
||||
image: dedicatedcode/reitti-tile-cache:5.9.1
|
||||
image: dedicatedcode/reitti-tile-cache:5.8.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
@@ -28,7 +28,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: reitti
|
||||
image: dedicatedcode/reitti:5.9.1
|
||||
image: dedicatedcode/reitti:5.8.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
@@ -7,7 +7,6 @@ metadata:
|
||||
app: vikunja-backup
|
||||
spec:
|
||||
schedule: "0 2 * * *"
|
||||
suspend: true
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
@@ -91,7 +90,6 @@ metadata:
|
||||
app: vikunja-backup
|
||||
spec:
|
||||
schedule: "0 3 * * *"
|
||||
suspend: true
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
labels:
|
||||
app: vikunja
|
||||
spec:
|
||||
replicas: 0
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: vikunja
|
||||
image: vikunja/vikunja:2.8.0
|
||||
image: vikunja/vikunja:2.7.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 3456
|
||||
|
||||
@@ -7,7 +7,7 @@ metadata:
|
||||
app: vikunja-db
|
||||
spec:
|
||||
serviceName: vikunja-db
|
||||
replicas: 0
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: vikunja-db
|
||||
|
||||
@@ -26,7 +26,14 @@ stringData:
|
||||
PAPERLESS_CONSUME_NFS_PATH: ENC[AES256_GCM,data:pUres8hqvlE8InL/hM+RCMlGVulsIGc=,iv:WWtAlN9W9z19+btNMrFJq6DuU+gJ5ff4KIMKjp1Yg5Q=,tag:6tOtmyELDEXAkFcO8P+QcA==,type:str]
|
||||
PAPERLESS_DATA_NFS_PATH: ENC[AES256_GCM,data:bjTpsnmlDwLLAHi4QeEFd2jabphhBperp8Aqwccmo/jX7gI=,iv:le84tRqtPvjyt/YCPZ4DIqNTOIQ1UI7NzKg/F405suY=,tag:qKBMVRQ6/jLWJvg5QmrmfA==,type:str]
|
||||
HOMEPAGE_HOST: ENC[AES256_GCM,data:lctHEKeFiwq47QYApI2/6YetrDQ=,iv:uPmTrf0ftl0T9jyOCHBGzb4cgn3qtKyPunFBZGMTkpY=,tag:O5PWGpULOjXv+xJHbIeepA==,type:str]
|
||||
GROCY_HOST: ENC[AES256_GCM,data:BE5AFwuZwG0TmzD3AeOcc1cT5CXf,iv:RIvwnpiLq0ICfbtlcMNAcbvSaQlm15ic8GYjCbm2Dg0=,tag:XUfN8hz6yrLZnkmo8ZLF0Q==,type:str]
|
||||
GROCY_NFS_PATH: ENC[AES256_GCM,data:qXHalcb1biQLv0mIgC/Tl14vwYFQddq6vpxJF+q0RgTk,iv:dHO7icOCGKOyVJYpMCSOwaL1UNBaaSahXCqucBYc3jI=,tag:95jW9bSUbIAFLTBo5y6jnQ==,type:str]
|
||||
CRYPTPAD_HOST: ENC[AES256_GCM,data:eIBnKk95IniY86Nu0VcwOInYLIb31wTG,iv:t4Mok6eHmVJXCR3mezjKOtLNUd0pHYAPFC+Hs+1wYLs=,tag:AbriiF/vCZgo1djxYBw+lQ==,type:str]
|
||||
CRYPTPAD_SANDBOX_HOST: ENC[AES256_GCM,data:60pg2HsZmMlC+uVgxyJzWqPKHWv1mebSJArH9J4G0kg=,iv:eprm7ESoYlt2oVt/Em3MGHA+hAMSdW+zfoCIZMyL4tg=,tag:5pJ4I953A++7dIFW5B7dMg==,type:str]
|
||||
CRYPTPAD_DATA_NFS_PATH: ENC[AES256_GCM,data:dNOxsmge//CgcBO0GIQUdcIBfaiGI1IVxXJ8IjmQl+Wh3A==,iv:hfuLuSWuSnbiItweWBSKxpxZJFgAWgUJCZNLcLZQVgw=,tag:7o353kHezQVRqb5BtC7rYw==,type:str]
|
||||
CRYPTPAD_CONFIG_NFS_PATH: ENC[AES256_GCM,data:VJ4h7ADenNgFIiNIFK7pJKMrUBYc4e9c4MdVzqGoR4TDWGYL,iv:8ZhMHiZLh2C4J/vh/8L96R6VIkKoWc7ib/bUAQ5rZE0=,tag:HgvJjjYybCgxatUbcRFY7Q==,type:str]
|
||||
SEERR_HOST: ENC[AES256_GCM,data:l64ttp+rLNU8GfwIE4fhJROSMDEb,iv:1vHOw0LyGN9OMhYemhtRq9GE2fc4J2EprZU3bp/h4kk=,tag:WNV0Jh7816ra7IIOBMspBg==,type:str]
|
||||
FIREFLY_HOST: ENC[AES256_GCM,data:EQdrW33PVlD/brZCqh/sTkqLdPWW/bo=,iv:zbNnjsT1J6lpCEWtvNyL4A3bjWsusCjI5goWcZ8hajk=,tag:VwA6YA056bsyYIKB+X59bw==,type:str]
|
||||
GITEA_HOST: ENC[AES256_GCM,data:Ky3tXS9E9iadabpVzoK5zEbigmtn,iv:t6OdQeQ7Kxzb7qbi+KeoOkJd0XE2Dse5P1fFyiHM6tg=,tag:GpmHtVDr0IQVbpbALVx40g==,type:str]
|
||||
BLOG_HOST: ENC[AES256_GCM,data:eADRFTla/PPFLVCoP9uKvPZ1YhY=,iv://xbCGLkZ6guGMDU0MnVtGR0adgMqNbe0zrCerj7w8k=,tag:HHXvN6y3YvJ4U4TkENkYVA==,type:str]
|
||||
VIKUNJA_HOST: ENC[AES256_GCM,data:1VVkvVrdx02jPxHsQoQI9zJKN9v/9mE=,iv:ZagDcljNclEGvmMzS6pJWTv+FMa/a/X6POaWb+kHJuo=,tag:RuaUoX8O4LrEs491Sp4UyQ==,type:str]
|
||||
@@ -46,6 +53,6 @@ sops:
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc
|
||||
encrypted_regex: ^(data|stringData|email)$
|
||||
lastmodified: "2026-11-05T17:33:51Z"
|
||||
mac: ENC[AES256_GCM,data:5t7xU5d1P5EdHvMc7lG2VGTrCKjJ7oPRDhPXAnY0pIb+OonO0aHZeJArl6GWefFMg22LMIvB7LIu9FkXpw35Ipq6KUWvgvXnGtH2DgbRSM/VQ3hMysH9xEZk6ZsviSV0fQMT0eU5UKwqNZnFk40JRz+Eao9YiMXfM12YiMUeUzQ=,iv:Xy6T62+PX14Qy3VFK6U9stKTWnBO/5VkmHgS+EFx7Fs=,tag:Rz/NMOXAs0EGIat+9fGZ2A==,type:str]
|
||||
lastmodified: "2026-10-18T10:53:13Z"
|
||||
mac: ENC[AES256_GCM,data:UqYOF0M6gaVjrE3/OQK/vQ0+lUcwan6fIvrJnm2BE6O28pDFL9iIDAo1ebp0oYhT8+cxNl/tTGlfH5TCblxpSCzu77/8eOgTdn19xqhTC8H8Yw56HVnYWNKZaQTyH+Qw6cG9HFf+d5OTYKeOEZufxZzpF7gT6M8BIssQ1vxEFQM=,iv:Vt1ln9ur5Dejod4X2+JUxwMwjlS198EWMASIw0Gf3Hk=,tag:NSnVkPLLTkHckVLEdTylWg==,type:str]
|
||||
version: 3.13.3
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -11,6 +11,7 @@
|
||||
"local>oleksandr/homelab//kubernetes/app/vikunja/renovate.json",
|
||||
"local>oleksandr/homelab//kubernetes/app/paperless/renovate.json",
|
||||
"local>oleksandr/homelab//kubernetes/app/lubelogger/renovate.json",
|
||||
"local>oleksandr/homelab//kubernetes/app/firefly/renovate.json",
|
||||
"local>oleksandr/homelab//kubernetes/app/podsync/renovate.json",
|
||||
"local>oleksandr/homelab//kubernetes/app/reitti/renovate.json",
|
||||
"local>oleksandr/homelab//kubernetes/app/dawarich/renovate.json"
|
||||
|
||||
Reference in New Issue
Block a user