Add OpenCloud backup configuration to Hetzner Object Storage
Created automated daily backup system using rclone and Kubernetes CronJob. Features: - Daily backups at 2 AM UTC - 7-day retention policy - Backs up data directory and configuration - Uses Hetzner S3-compatible Object Storage - Read-only access to OpenCloud volumes Files: - backup-cronjob.yaml: CronJob for automated backups - BACKUP.md: Complete setup and restore documentation Requires: - Hetzner Object Storage bucket credentials (sealed secret) - S3 access key/secret to be provided Once credentials are configured, backups will run automatically. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
e7a888d479
commit
9e36a16d28
2 changed files with 266 additions and 0 deletions
174
apps/opencloud/BACKUP.md
Normal file
174
apps/opencloud/BACKUP.md
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
# OpenCloud Backup Configuration
|
||||
|
||||
## Overview
|
||||
|
||||
Daily automated backup of OpenCloud data to Hetzner Object Storage using rclone.
|
||||
|
||||
## Schedule
|
||||
|
||||
- **Frequency:** Daily at 2:00 AM UTC
|
||||
- **Retention:** Last 7 days of backups
|
||||
- **Method:** rclone sync for data, rclone copy for config
|
||||
|
||||
## What Gets Backed Up
|
||||
|
||||
1. **Data Directory** (`/var/lib/opencloud/data`)
|
||||
- User files
|
||||
- Shares
|
||||
- All uploaded content
|
||||
|
||||
2. **Configuration** (`/etc/opencloud`)
|
||||
- OpenCloud configuration files
|
||||
- Note: Secrets are NOT backed up (stored in Kubernetes secrets)
|
||||
|
||||
## Setup Required
|
||||
|
||||
### 1. Create Hetzner Object Storage Bucket
|
||||
|
||||
```bash
|
||||
# Create bucket via Hetzner Cloud Console or CLI
|
||||
# Bucket name: opencloud-backup
|
||||
# Region: fsn1 (or your preferred region)
|
||||
```
|
||||
|
||||
### 2. Create S3 Access Credentials
|
||||
|
||||
Generate S3-compatible access credentials from Hetzner Object Storage console.
|
||||
|
||||
### 3. Create Sealed Secret
|
||||
|
||||
```bash
|
||||
# Create unsealed secret first
|
||||
kubectl create secret generic hetzner-s3-credentials \
|
||||
--from-literal=access-key='YOUR_ACCESS_KEY' \
|
||||
--from-literal=secret-key='YOUR_SECRET_KEY' \
|
||||
--namespace=opencloud \
|
||||
--dry-run=client -o yaml > hetzner-s3-credentials.yaml
|
||||
|
||||
# Seal it
|
||||
kubeseal --cert /tmp/sealed-secrets-cert.pem \
|
||||
-f hetzner-s3-credentials.yaml \
|
||||
-o yaml > hetzner-s3-credentials-sealed.yaml
|
||||
|
||||
# Apply sealed secret
|
||||
kubectl apply -f hetzner-s3-credentials-sealed.yaml
|
||||
|
||||
# Clean up unsealed secret
|
||||
rm hetzner-s3-credentials.yaml
|
||||
```
|
||||
|
||||
### 4. Update Endpoint in backup-cronjob.yaml
|
||||
|
||||
Replace `https://fsn1.your-objectstorage.com` with your actual Hetzner Object Storage endpoint:
|
||||
- fsn1: `https://fsn1.your-objectstorage.com`
|
||||
- nbg1: `https://nbg1.your-objectstorage.com`
|
||||
- hel1: `https://hel1.your-objectstorage.com`
|
||||
|
||||
### 5. Deploy Backup CronJob
|
||||
|
||||
```bash
|
||||
kubectl apply -f backup-cronjob.yaml
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
### Check CronJob Status
|
||||
|
||||
```bash
|
||||
kubectl get cronjob -n opencloud
|
||||
kubectl get jobs -n opencloud
|
||||
```
|
||||
|
||||
### View Backup Logs
|
||||
|
||||
```bash
|
||||
# Get the latest backup job
|
||||
kubectl get jobs -n opencloud -l job-name=opencloud-backup
|
||||
|
||||
# View logs
|
||||
kubectl logs -n opencloud job/opencloud-backup-<timestamp>
|
||||
```
|
||||
|
||||
### Trigger Manual Backup
|
||||
|
||||
```bash
|
||||
kubectl create job -n opencloud \
|
||||
--from=cronjob/opencloud-backup \
|
||||
opencloud-backup-manual-$(date +%s)
|
||||
```
|
||||
|
||||
## Restore Procedure
|
||||
|
||||
### Restore Data from Backup
|
||||
|
||||
1. Scale down OpenCloud deployment:
|
||||
```bash
|
||||
kubectl scale deployment/opencloud -n opencloud --replicas=0
|
||||
```
|
||||
|
||||
2. Create restore job:
|
||||
```bash
|
||||
kubectl run opencloud-restore \
|
||||
--image=rclone/rclone:latest \
|
||||
--restart=Never \
|
||||
--namespace=opencloud \
|
||||
--env="S3_ACCESS_KEY=YOUR_KEY" \
|
||||
--env="S3_SECRET_KEY=YOUR_SECRET" \
|
||||
--overrides='
|
||||
{
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "restore",
|
||||
"image": "rclone/rclone:latest",
|
||||
"command": ["/bin/sh", "-c"],
|
||||
"args": ["
|
||||
cat > /root/.config/rclone/rclone.conf <<EOC
|
||||
[hetzner-s3]
|
||||
type = s3
|
||||
provider = Other
|
||||
access_key_id = ${S3_ACCESS_KEY}
|
||||
secret_access_key = ${S3_SECRET_KEY}
|
||||
endpoint = https://fsn1.your-objectstorage.com
|
||||
region = fsn1
|
||||
EOC
|
||||
rclone sync hetzner-s3:opencloud-backup/data-YYYYMMDD-HHMMSS /data
|
||||
"],
|
||||
"volumeMounts": [{
|
||||
"name": "data",
|
||||
"mountPath": "/data"
|
||||
}]
|
||||
}],
|
||||
"volumes": [{
|
||||
"name": "data",
|
||||
"persistentVolumeClaim": {
|
||||
"claimName": "opencloud-data"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
3. Scale up OpenCloud:
|
||||
```bash
|
||||
kubectl scale deployment/opencloud -n opencloud --replicas=1
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
Monitor backup success/failure via:
|
||||
- Kubernetes Job status
|
||||
- Backup pod logs
|
||||
- Hetzner Object Storage bucket size/contents
|
||||
|
||||
## Estimated Costs
|
||||
|
||||
- **Storage:** ~€0.0059/GB/month (Hetzner Object Storage)
|
||||
- **Egress:** Free for first 1TB/month
|
||||
- **Example:** 50GB of data × 7 days retention = ~€2-3/month
|
||||
|
||||
## Notes
|
||||
|
||||
- Backups are encrypted in transit (HTTPS)
|
||||
- Backups at rest encryption depends on Hetzner Object Storage settings
|
||||
- Consider enabling versioning on the S3 bucket for additional protection
|
||||
- The backup job has read-only access to OpenCloud data
|
||||
92
apps/opencloud/backup-cronjob.yaml
Normal file
92
apps/opencloud/backup-cronjob.yaml
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
---
|
||||
# OpenCloud Daily Backup to Hetzner Object Storage
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: opencloud-backup
|
||||
namespace: opencloud
|
||||
spec:
|
||||
schedule: "0 2 * * *" # Daily at 2 AM UTC
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: backup
|
||||
image: rclone/rclone:latest
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
DATE=$(date +%Y%m%d-%H%M%S)
|
||||
echo "Starting OpenCloud backup at $DATE"
|
||||
|
||||
# Configure rclone for Hetzner S3
|
||||
cat > /root/.config/rclone/rclone.conf <<EOC
|
||||
[hetzner-s3]
|
||||
type = s3
|
||||
provider = Other
|
||||
access_key_id = ${S3_ACCESS_KEY}
|
||||
secret_access_key = ${S3_SECRET_KEY}
|
||||
endpoint = https://fsn1.your-objectstorage.com
|
||||
region = fsn1
|
||||
acl = private
|
||||
EOC
|
||||
|
||||
# Backup OpenCloud data directory
|
||||
echo "Backing up data directory..."
|
||||
rclone sync /data hetzner-s3:opencloud-backup/data-${DATE} \
|
||||
--progress \
|
||||
--transfers 8 \
|
||||
--checkers 8 \
|
||||
--stats 1m
|
||||
|
||||
# Backup configuration
|
||||
echo "Backing up configuration..."
|
||||
rclone copy /config hetzner-s3:opencloud-backup/config-${DATE} \
|
||||
--progress
|
||||
|
||||
# Keep only last 7 days of backups
|
||||
echo "Cleaning old backups (keeping last 7 days)..."
|
||||
rclone delete hetzner-s3:opencloud-backup \
|
||||
--min-age 7d \
|
||||
--rmdirs
|
||||
|
||||
echo "Backup completed successfully at $(date)"
|
||||
env:
|
||||
- name: S3_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: hetzner-s3-credentials
|
||||
key: access-key
|
||||
- name: S3_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: hetzner-s3-credentials
|
||||
key: secret-key
|
||||
volumeMounts:
|
||||
- name: opencloud-data
|
||||
mountPath: /data
|
||||
readOnly: true
|
||||
- name: opencloud-config
|
||||
mountPath: /config
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
volumes:
|
||||
- name: opencloud-data
|
||||
persistentVolumeClaim:
|
||||
claimName: opencloud-data
|
||||
- name: opencloud-config
|
||||
configMap:
|
||||
name: opencloud-config
|
||||
Loading…
Add table
Reference in a new issue