Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions scripts/deploy/backup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/bin/bash
# backup.sh - PostgreSQL backup script for EnvSync K3S deployment
# Dumps the database to local disk and prunes backups older than 7 days
set -euo pipefail

# Timestamp used for the backup folder name e.g. 2026-03-23_11-55-00
DATE=$(date +%Y-%m-%d_%H-%M-%S)

# Full path where this backup will be stored
BACKUP_DIR="/var/backups/envsync/${DATE}"

# Kubernetes namespace where EnvSync pods are running
NAMESPACE="envsync"

# Dynamically find the postgres pod name — never hardcode pod names,
# Kubernetes regenerates them on every restart
POSTGRES_POD=$(kubectl get pod -n "$NAMESPACE" \
-l app=postgres \
--no-headers \
-o custom-columns=":metadata.name")

echo "Starting EnvSync backup: ${DATE}"

# -p creates parent directories if they don't exist
# and won't error if the folder already exists
mkdir -p "${BACKUP_DIR}"

echo "Dumping PostgreSQL..."

# kubectl exec runs a command inside the running postgres pod
# The -- separates kubectl flags from the command being run inside the pod
# Output is redirected into a .sql file in our backup directory
kubectl exec -n "$NAMESPACE" "$POSTGRES_POD" -- \
pg_dump -U postgres envsync \
> "${BACKUP_DIR}/envsync_db.sql"

echo "Pruning backups older than 7 days..."

# find options explained:
# -maxdepth 1 only look one level deep, don't recurse into backup folders
# -type d only match directories, not files
# -mtime +7 only match directories older than 7 days
# -exec rm -rf {} + delete each match
find /var/backups/envsync -maxdepth 1 -type d -mtime +7 -exec rm -rf {} +

echo "Backup complete: ${BACKUP_DIR}"

# Show what was saved with human-readable file sizes
ls -lh "${BACKUP_DIR}"
103 changes: 103 additions & 0 deletions scripts/deploy/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/bin/bash

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This script only creates the reverse proxy, It also needs to create pods for older container like postgres, clickstack, zetadel, etc

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review you're right, the deployment and service manifests for postgres, Zitadel, Redis, RustFS, and the app containers are missing. I'll add those to init.sh now.

# init.sh - Initializes a fresh Linux server with K3S and deploys EnvSync
# Platform agnostic - works on any Ubuntu/Debian server
# Usage: sudo bash init.sh
set -euo pipefail

# Ask the operator for the root domain at runtime
# All subdomains will be generated from this e.g. api.envsync.cloud
echo "============================================"
echo " EnvSync K3S Deployment Setup "
echo "============================================"
read -rp "Enter your root domain (e.g. envsync.cloud): " ROOT_DOMAIN

# Confirm before proceeding
echo ""
echo "Deploying EnvSync to the following subdomains:"
echo " API: api.${ROOT_DOMAIN}"
echo " Auth: auth.${ROOT_DOMAIN}"
echo " S3: s3.${ROOT_DOMAIN}"
echo " Web app: app.${ROOT_DOMAIN}"
echo ""
read -rp "Continue? (y/n): " CONFIRM
if [[ "$CONFIRM" != "y" ]]; then
echo "Aborted."
exit 0
fi

echo "Installing K3S..."

# K3S is a lightweight single-node Kubernetes distribution
# --write-kubeconfig-mode 644 makes the kubeconfig readable without sudo
curl -sfL https://get.k3s.io | sh -s - \
--write-kubeconfig-mode 644

echo "Waiting for K3S to be ready..."

# Loop until the node reports Ready status
# This usually takes 20-30 seconds on first boot
until kubectl get node | grep -q "Ready"; do
echo " ...waiting for node"
sleep 5
done

echo "K3S is ready."

# Directory where our manifests live relative to this script
MANIFEST_DIR="$(dirname "$0")/manifests"

echo "Injecting domain into manifests..."

# sed replaces the PLACEHOLDER values in the manifest files
# with the actual domain the operator entered
# -i edits the files in place
sed -i "s/PLACEHOLDER_API_DOMAIN/api.${ROOT_DOMAIN}/g" "$MANIFEST_DIR/13-ingress.yaml"
sed -i "s/PLACEHOLDER_APP_DOMAIN/app.${ROOT_DOMAIN}/g" "$MANIFEST_DIR/13-ingress.yaml"
sed -i "s/PLACEHOLDER_AUTH_DOMAIN/auth.${ROOT_DOMAIN}/g" "$MANIFEST_DIR/13-ingress.yaml"
sed -i "s/PLACEHOLDER_S3_DOMAIN/s3.${ROOT_DOMAIN}/g" "$MANIFEST_DIR/13-ingress.yaml"

# Also inject the auth domain into zitadel so it knows its external URL
sed -i "s/PLACEHOLDER_AUTH_DOMAIN/auth.${ROOT_DOMAIN}/g" "$MANIFEST_DIR/10-zitadel.yaml"

# Also inject the api domain into envsync-web so it knows where the API is
sed -i "s/PLACEHOLDER_API_DOMAIN/api.${ROOT_DOMAIN}/g" "$MANIFEST_DIR/12-envsync-web.yaml"

echo "Applying manifests in dependency order..."

# Apply each manifest in numbered order
# Each number prefix ensures correct dependency sequencing
for manifest in "$MANIFEST_DIR"/*.yaml; do
echo " Applying $(basename "$manifest")..."
kubectl apply -f "$manifest"
done

echo "Waiting for core pods to be ready..."

# Wait for postgres first since everything depends on it
kubectl wait --namespace envsync \
--for=condition=ready pod \
--selector=app=postgres \
--timeout=120s

# Wait for envsync-api last since it depends on all other services
kubectl wait --namespace envsync \
--for=condition=ready pod \
--selector=app=envsync-api \
--timeout=180s

echo "Pods are ready."

# Run EnvSync's own initialization sequence
# This creates Zitadel OIDC apps and writes client IDs to .env
echo "Running EnvSync init..."
bun run cli init

echo ""
echo "============================================"
echo " EnvSync deployed successfully!"
echo "============================================"
echo " API: https://api.${ROOT_DOMAIN}"
echo " Auth: https://auth.${ROOT_DOMAIN}"
echo " S3: https://s3.${ROOT_DOMAIN}"
echo " Web app: https://app.${ROOT_DOMAIN}"
echo "============================================"
8 changes: 8 additions & 0 deletions scripts/deploy/manifests/00-secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: envsync-secrets
namespace: envsync
type: Opaque
stringData:
MINIKMS_ROOT_KEY: "changeme-replace-with-real-32-char-key"
4 changes: 4 additions & 0 deletions scripts/deploy/manifests/01-namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: envsync
39 changes: 39 additions & 0 deletions scripts/deploy/manifests/02-postgres.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: envsync
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:17
env:
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
value: postgres
- name: POSTGRES_DB
value: envsync
ports:
- containerPort: 5432
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: envsync
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
32 changes: 32 additions & 0 deletions scripts/deploy/manifests/03-redis.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: envsync
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:latest
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: envsync
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
39 changes: 39 additions & 0 deletions scripts/deploy/manifests/04-zitadel-db.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: zitadel-db
namespace: envsync
spec:
replicas: 1
selector:
matchLabels:
app: zitadel-db
template:
metadata:
labels:
app: zitadel-db
spec:
containers:
- name: zitadel-db
image: postgres:17
env:
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
value: postgres
- name: POSTGRES_DB
value: zitadel
ports:
- containerPort: 5432
---
apiVersion: v1
kind: Service
metadata:
name: zitadel-db
namespace: envsync
spec:
selector:
app: zitadel-db
ports:
- port: 5432
targetPort: 5432
39 changes: 39 additions & 0 deletions scripts/deploy/manifests/05-minikms-db.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: minikms-db
namespace: envsync
spec:
replicas: 1
selector:
matchLabels:
app: minikms-db
template:
metadata:
labels:
app: minikms-db
spec:
containers:
- name: minikms-db
image: postgres:17
env:
- name: POSTGRES_USER
value: postgres
- name: POSTGRES_PASSWORD
value: postgres
- name: POSTGRES_DB
value: minikms
ports:
- containerPort: 5432
---
apiVersion: v1
kind: Service
metadata:
name: minikms-db
namespace: envsync
spec:
selector:
app: minikms-db
ports:
- port: 5432
targetPort: 5432
39 changes: 39 additions & 0 deletions scripts/deploy/manifests/06-openfga-db.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: openfga-db
namespace: envsync
spec:
replicas: 1
selector:
matchLabels:
app: openfga-db
template:
metadata:
labels:
app: openfga-db
spec:
containers:
- name: openfga-db
image: postgres:17
env:
- name: POSTGRES_USER
value: openfga
- name: POSTGRES_PASSWORD
value: openfga
- name: POSTGRES_DB
value: openfga
ports:
- containerPort: 5432
---
apiVersion: v1
kind: Service
metadata:
name: openfga-db
namespace: envsync
spec:
selector:
app: openfga-db
ports:
- port: 5432
targetPort: 5432
Loading
Loading