Skip to content

Instantly share code, notes, and snippets.

@Jordan-Hall
Last active October 14, 2025 23:19
Show Gist options
  • Select an option

  • Save Jordan-Hall/62f8c5f1f21f3165325aaca47e241d5d to your computer and use it in GitHub Desktop.

Select an option

Save Jordan-Hall/62f8c5f1f21f3165325aaca47e241d5d to your computer and use it in GitHub Desktop.
#!/bin/bash
set -e
# -------------------------
# DEFAULT CONFIGURATION
# -------------------------
HOST_IP_DEFAULT=$(hostname -I | awk '{print $1}')
DOMAIN_DEFAULT="$HOST_IP_DEFAULT.sslip.io"
EMAIL_DEFAULT="jordan@libertyware.co.uk"
ADMIN_USER_DEFAULT="admin"
ADMIN_PASS_DEFAULT="admin123"
SURREALDB_REPLICAS_DEFAULT=3
COOLIFY_REPLICAS_DEFAULT=1
KUBERO_REPLICAS_DEFAULT=1
PD_REPLICAS_DEFAULT=3
TIKV_REPLICAS_DEFAULT=3
STORAGE_PATH_DEFAULT="/mnt/tikv-data"
K3S_MASTER_TOKEN_FILE="/root/k3s_token.txt"
K3S_MASTER_IP_FILE="/root/k3s_master_ip.txt"
# -------------------------
# USER PARAMETERS
# -------------------------
HOST_IP="${HOST_IP:-$HOST_IP_DEFAULT}"
DOMAIN="${DOMAIN:-$DOMAIN_DEFAULT}"
EMAIL="${EMAIL:-$EMAIL_DEFAULT}"
ADMIN_USER="${ADMIN_USER:-$ADMIN_USER_DEFAULT}"
ADMIN_PASS="${ADMIN_PASS:-$ADMIN_PASS_DEFAULT}"
SURREALDB_REPLICAS="${SURREALDB_REPLICAS:-$SURREALDB_REPLICAS_DEFAULT}"
COOLIFY_REPLICAS="${COOLIFY_REPLICAS:-$COOLIFY_REPLICAS_DEFAULT}"
KUBERO_REPLICAS="${KUBERO_REPLICAS:-$KUBERO_REPLICAS_DEFAULT}"
PD_REPLICAS="${PD_REPLICAS:-$PD_REPLICAS_DEFAULT}"
TIKV_REPLICAS="${TIKV_REPLICAS:-$TIKV_REPLICAS_DEFAULT}"
STORAGE_PATH="${STORAGE_PATH:-$STORAGE_PATH_DEFAULT}"
# -------------------------
# SYSTEM UPDATE & FIREWALL
# -------------------------
echo "Updating system..."
sudo apt update -y && sudo apt upgrade -y
sudo apt install -y curl wget gnupg lsb-release ufw software-properties-common apt-transport-https
echo "Configuring firewall..."
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 6443/tcp
sudo ufw allow 80,443/tcp
sudo ufw --force enable
# -------------------------
# DOCKER INSTALLATION
# -------------------------
if ! command -v docker >/dev/null 2>&1; then
echo "Installing Docker..."
sudo apt remove -y docker docker-engine docker.io containerd runc || true
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update -y
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl enable docker
sudo systemctl start docker
fi
# -------------------------
# K3S INSTALL FUNCTIONS
# -------------------------
install_k3s_master() {
echo "Installing K3s master..."
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--write-kubeconfig-mode 644" sh -
sleep 10
echo $(sudo cat /var/lib/rancher/k3s/server/node-token) > $K3S_MASTER_TOKEN_FILE
echo $HOST_IP > $K3S_MASTER_IP_FILE
}
install_k3s_agent() {
local master_ip="$1"
local token="$2"
echo "Installing K3s agent..."
curl -sfL https://get.k3s.io | K3S_URL="https://$master_ip:6443" K3S_TOKEN="$token" sh -
}
wait_nodes_ready() {
echo "Waiting for all nodes to be ready..."
kubectl wait --for=condition=Ready nodes --all --timeout=180s
}
# -------------------------
# INSTALL K3S
# -------------------------
if [ -f /etc/rancher/k3s/k3s.yaml ]; then
echo "K3s already installed."
else
if [ ! -f $K3S_MASTER_TOKEN_FILE ]; then
install_k3s_master
else
MASTER_IP=$(cat $K3S_MASTER_IP_FILE)
K3S_TOKEN=$(cat $K3S_MASTER_TOKEN_FILE)
install_k3s_agent $MASTER_IP $K3S_TOKEN
fi
fi
wait_nodes_ready
# -------------------------
# CREATE NAMESPACES
# -------------------------
for ns in tikv surrealdb coolify kubero cert-manager monitoring; do
kubectl create namespace $ns --dry-run=client -o yaml | kubectl apply -f -
done
# -------------------------
# INSTALL KUBERO CLI
# -------------------------
if ! command -v kubero >/dev/null 2>&1; then
echo "Installing Kubero CLI..."
curl -fsSL https://get.kubero.dev | bash
fi
# -------------------------
# INSTALL KUBERO COMPONENTS
# -------------------------
echo "Installing Kubero components via Kubero CLI..."
kubero install -c kubernetes --domain $DOMAIN
kubero install -c olm --domain $DOMAIN
kubero install -c ingress --domain $DOMAIN
kubero install -c metrics --domain $DOMAIN
kubero install -c certmanager --domain $DOMAIN
kubero install -c kubero-operator --domain $DOMAIN
kubero install -c monitoring --domain $DOMAIN
kubero install -c kubero-ui --domain $DOMAIN --user $ADMIN_USER --user-password $ADMIN_PASS
# -------------------------
# DEPLOY PD (Placement Driver)
# -------------------------
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: pd
namespace: tikv
spec:
serviceName: pd
replicas: $PD_REPLICAS
selector:
matchLabels:
app: pd
template:
metadata:
labels:
app: pd
spec:
containers:
- name: pd
image: pingcap/pd:latest
args:
- --name=pd
- --data-dir=/pd/data
- --client-urls=http://0.0.0.0:2379
- --peer-urls=http://0.0.0.0:2380
- --initial-cluster=pd-0=http://pd-0.pd.tikv.svc.cluster.local:2380,pd-1=http://pd-1.pd.tikv.svc.cluster.local:2380,pd-2=http://pd-2.pd.tikv.svc.cluster.local:2380
ports:
- containerPort: 2379
- containerPort: 2380
volumeMounts:
- name: pd-data
mountPath: /pd/data
volumeClaimTemplates:
- metadata:
name: pd-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: Service
metadata:
name: pd
namespace: tikv
spec:
clusterIP: None
selector:
app: pd
ports:
- port: 2379
targetPort: 2379
EOF
# -------------------------
# DEPLOY TiKV
# -------------------------
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: tikv
namespace: tikv
spec:
serviceName: tikv
replicas: $TIKV_REPLICAS
selector:
matchLabels:
app: tikv
template:
metadata:
labels:
app: tikv
spec:
containers:
- name: tikv
image: pingcap/tikv:latest
args:
- --pd=pd.tikv.svc.cluster.local:2379
- --addr=0.0.0.0:20160
- --data-dir=/tikv/data
ports:
- containerPort: 20160
volumeMounts:
- name: tikv-data
mountPath: /tikv/data
volumeClaimTemplates:
- metadata:
name: tikv-data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: tikv
namespace: tikv
spec:
clusterIP: None
selector:
app: tikv
ports:
- port: 20160
targetPort: 20160
EOF
# -------------------------
# DEPLOY SURREALDB
# -------------------------
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: surrealdb
namespace: surrealdb
spec:
replicas: $SURREALDB_REPLICAS
selector:
matchLabels:
app: surrealdb
template:
metadata:
labels:
app: surrealdb
spec:
containers:
- name: surrealdb
image: surrealdb/surrealdb:latest
args:
- "--log"
- "info"
- "--bind"
- "0.0.0.0:8000"
- "--store"
- "tikv://pd.tikv.svc.cluster.local:2379"
ports:
- containerPort: 8000
EOF
# -------------------------
# DEPLOY COOLIFY
# -------------------------
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: coolify
namespace: coolify
spec:
replicas: $COOLIFY_REPLICAS
selector:
matchLabels:
app: coolify
template:
metadata:
labels:
app: coolify
spec:
containers:
- name: coolify
image: coollabsio/coolify:latest
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: coolify
namespace: coolify
spec:
selector:
app: coolify
ports:
- port: 80
targetPort: 3000
EOF
# -------------------------
# DEPLOY KUBERO (DASHBOARD + OPTIONAL MODULES)
# -------------------------
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: kubero
namespace: kubero
spec:
replicas: $KUBERO_REPLICAS
selector:
matchLabels:
app: kubero
template:
metadata:
labels:
app: kubero
spec:
containers:
- name: kubero
image: kubero-dev/kubero:latest
ports:
- containerPort: 8080
- name: kubero-dashboard
image: kubero-dev/kubero-dashboard:latest
ports:
- containerPort: 9090
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: kubero
namespace: kubero
spec:
selector:
app: kubero
ports:
- port: 8080
targetPort: 8080
EOF
# -------------------------
# DEPLOY PROMETHEUS + GRAFANA
# -------------------------
kubectl apply -f https://github.com/prometheus-operator/prometheus-operator/raw/main/bundle.yaml
kubectl wait --for=condition=Ready pods --all -n monitoring --timeout=180s
cat <<EOF | kubectl apply -f -
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: tikv-prometheus
namespace: monitoring
spec:
serviceAccountName: prometheus
resources:
requests:
memory: 400Mi
EOF
# -------------------------
# WAIT FOR ALL PODS READY
# -------------------------
echo "Waiting for all pods in all namespaces..."
kubectl wait --for=condition=Ready pods --all --timeout=300s
echo "✅ Full production-ready cluster installed!"
echo "Coolify: https://coolify.$DOMAIN"
echo "SurrealDB: https://surrealdb.$DOMAIN"
echo "Kubero Dashboard: https://kubero.$DOMAIN"
echo "Grafana: https://grafana.$DOMAIN"
echo "🎉 Multi-node ready — copy k3s_token.txt & k3s_master_ip.txt to new nodes to join agents."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment