Last active
October 14, 2025 23:19
-
-
Save Jordan-Hall/62f8c5f1f21f3165325aaca47e241d5d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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