├── .5stack-env.config.test ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── LICENSE ├── README.md ├── base ├── api │ ├── deployment.yaml │ ├── ingress-ws.yaml │ ├── ingress.yaml │ ├── kustomization.yaml │ ├── rbac │ │ ├── cluster-role-binding.yaml │ │ ├── cluster-role.yaml │ │ ├── role-binding.yaml │ │ ├── role.yaml │ │ └── service-accounts.yaml │ └── service.yaml ├── game-server-node-connector │ ├── daemonset.yaml │ ├── kustomization.yaml │ ├── rbac │ │ ├── cluster-role-binding.yaml │ │ ├── cluster-role.yaml │ │ ├── role-binding.yaml │ │ ├── role.yaml │ │ └── service-accounts.yaml │ └── service.yaml ├── hasura │ ├── deployment.yaml │ ├── kustomization.yaml │ └── service.yaml ├── kustomization.yaml ├── minio │ ├── ingress.yaml │ ├── kustomization.yaml │ ├── service.yaml │ └── stateful-set.yaml ├── namespace.yaml ├── nginx │ ├── kustomization.yaml │ └── service.yaml ├── properties │ ├── api-config.env.example │ ├── nginx-config.env.example │ ├── s3-config.env.example │ └── typesense-config.env.example ├── redis │ ├── configmap.yaml │ ├── deployment.yaml │ ├── kustomization.yaml │ └── service.yaml ├── secrets │ ├── api-secrets.env.example │ ├── discord-secrets.env.example │ ├── faceit-secrets.env.example │ ├── hasura-secrets.env.example │ ├── minio-secrets.env.example │ ├── redis-secrets.env.example │ ├── s3-secrets.env.example │ ├── steam-secrets.env.example │ ├── tailscale-secrets.env.example │ ├── timescaledb-secrets.env.example │ └── typesense-secrets.env.example ├── timescaledb │ ├── kustomization.yaml │ ├── service.yaml │ └── stateful-set.yaml ├── typesense │ ├── configmap.yaml │ ├── ingress.yaml │ ├── kustomization.yaml │ ├── service.yaml │ └── stateful-set.yaml ├── volumes │ ├── kustomization.yaml │ ├── minio-pv.yaml │ ├── timescaledb-pv.yaml │ └── typesense-pv.yaml └── web │ ├── deployment.yaml │ ├── ingress.yaml │ ├── kustomization.yaml │ └── service.yaml ├── custom.sh ├── debug.sh ├── dev.sh ├── fix-ssl.sh ├── fix-versions.sh ├── game-node-server-setup.sh ├── install-nvidia.sh ├── install.sh ├── overlays ├── cert-manager │ ├── cert-manager.yaml │ ├── certificate.yaml │ ├── ingress-patch.yaml │ ├── issuer.yaml │ └── kustomization.yaml ├── dev │ ├── dev-cs-server │ │ ├── deployment.yaml │ │ ├── dev-server-secrets.env.example │ │ └── kustomization.yaml │ ├── dev-game-stream │ │ ├── dev-game-stream-secrets.env.example │ │ ├── kustomization.yaml │ │ ├── service.yaml │ │ └── stateful-set.yaml │ ├── game-server-node-connector │ │ ├── deployment.yaml │ │ └── kustomization.yaml │ └── kustomization.yaml ├── nvidia │ ├── kustomization.yaml │ └── nvidia-plugin.yaml └── weblate │ ├── deployment.yaml │ ├── ingress.yaml │ ├── kustomization.yaml │ ├── properties │ └── weblate-config.env copy.example │ └── service.yaml ├── setup-env.sh ├── update.sh └── weblate.sh /.5stack-env.config.test: -------------------------------------------------------------------------------- 1 | REVERSE_PROXY=true 2 | KUBECONFIG=/Users/luke/.kube/5stackgg 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: lukepolo 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: '[BUG] ' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | ## Describe the Bug 10 | A clear and concise description of what the bug is. 11 | 12 | 13 | ## Environment 14 | - OS: [e.g. Ubuntu 20.04.6] 15 | - 5Stack Plugin Version: [e.g. v0.0.1] 16 | 17 | ## Debug Log 18 | Run the debug script and upload the debug file. `./debug.sh`, view the docs https://docs.5stack.gg/install/debugging 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '[FEATURE] ' 5 | labels: 'enhancement' 6 | assignees: '' 7 | --- 8 | 9 | ## Is your feature request related to a problem? Please describe. 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | ## Describe the solution you'd like 13 | A clear and concise description of what you want to happen. 14 | 15 | ## Describe alternatives you've considered 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | ## Additional context 19 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore actual env and secret files 2 | .env 3 | *.env 4 | 5 | # Don't ignore example files 6 | !*.env.example 7 | 8 | kustomize 9 | 10 | custom 11 | 12 | .5stack-env.config -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 5Stack.gg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 5Stack a Counter-Strike Panel 2 | 3 | Vist https://docs.5stack.gg for documentation. 4 | -------------------------------------------------------------------------------- /base/api/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: api 6 | name: api 7 | namespace: 5stack 8 | spec: 9 | replicas: 1 10 | strategy: 11 | type: RollingUpdate 12 | rollingUpdate: 13 | maxUnavailable: 0 14 | maxSurge: 1 15 | selector: 16 | matchLabels: 17 | app: api 18 | template: 19 | metadata: 20 | labels: 21 | app: api 22 | spec: 23 | serviceAccountName: server-creator 24 | affinity: 25 | nodeAffinity: 26 | requiredDuringSchedulingIgnoredDuringExecution: 27 | nodeSelectorTerms: 28 | - matchExpressions: 29 | - key: 5stack-api 30 | operator: In 31 | values: 32 | - "true" 33 | containers: 34 | - image: ghcr.io/5stackgg/api:latest 35 | name: api 36 | ports: 37 | - containerPort: 5585 38 | - containerPort: 5586 39 | env: 40 | - name: POD_NAME 41 | valueFrom: 42 | fieldRef: 43 | fieldPath: metadata.name 44 | envFrom: 45 | - configMapRef: 46 | name: api-config 47 | - secretRef: 48 | name: api-secrets 49 | - secretRef: 50 | name: steam-secrets 51 | - secretRef: 52 | name: hasura-secrets 53 | - secretRef: 54 | name: typesense-secrets 55 | - secretRef: 56 | name: discord-secrets 57 | - secretRef: 58 | name: redis-secrets 59 | - secretRef: 60 | name: timescaledb-secrets 61 | - secretRef: 62 | name: tailscale-secrets 63 | - secretRef: 64 | name: s3-secrets 65 | - configMapRef: 66 | name: s3-config 67 | - secretRef: 68 | name: faceit-secrets -------------------------------------------------------------------------------- /base/api/ingress-ws.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: ws 5 | annotations: 6 | nginx.ingress.kubernetes.io/rewrite-target: /ws/$1 7 | namespace: 5stack 8 | spec: 9 | ingressClassName: nginx 10 | rules: 11 | - host: $(WS_DOMAIN) 12 | http: 13 | paths: 14 | - backend: 15 | service: 16 | name: api 17 | port: 18 | number: 5585 19 | path: /(.*) 20 | pathType: ImplementationSpecific -------------------------------------------------------------------------------- /base/api/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: api 5 | namespace: 5stack 6 | spec: 7 | ingressClassName: nginx 8 | rules: 9 | - host: $(API_DOMAIN) 10 | http: 11 | paths: 12 | - backend: 13 | service: 14 | name: api 15 | port: 16 | number: 5585 17 | path: /me 18 | pathType: Exact 19 | - backend: 20 | service: 21 | name: api 22 | port: 23 | number: 5585 24 | path: /matches 25 | pathType: Prefix 26 | - backend: 27 | service: 28 | name: hasura 29 | port: 30 | number: 8080 31 | path: /v1 32 | pathType: Prefix 33 | - backend: 34 | service: 35 | name: hasura 36 | port: 37 | number: 8080 38 | path: /v1alpha1 39 | pathType: Prefix 40 | - backend: 41 | service: 42 | name: hasura 43 | port: 44 | number: 8080 45 | path: /v2 46 | pathType: Prefix 47 | - backend: 48 | service: 49 | name: api 50 | port: 51 | number: 5585 52 | path: /game-server-node/script 53 | pathType: Prefix 54 | - backend: 55 | service: 56 | name: api 57 | port: 58 | number: 5585 59 | path: /game-server-node/ping 60 | pathType: Prefix 61 | - host: $(WEB_DOMAIN) 62 | http: 63 | paths: 64 | - backend: 65 | service: 66 | name: api 67 | port: 68 | number: 5585 69 | path: /auth 70 | pathType: Prefix 71 | - backend: 72 | service: 73 | name: api 74 | port: 75 | number: 5585 76 | path: /quick-connect 77 | pathType: Exact 78 | - backend: 79 | service: 80 | name: api 81 | port: 82 | number: 5585 83 | path: /telemetry 84 | pathType: Exact 85 | - backend: 86 | service: 87 | name: api 88 | port: 89 | number: 5585 90 | path: /discord-invite 91 | pathType: Exact 92 | - backend: 93 | service: 94 | name: api 95 | port: 96 | number: 5585 97 | path: /discord-bot 98 | pathType: Exact 99 | - backend: 100 | service: 101 | name: api 102 | port: 103 | number: 5585 104 | path: /matches/[^/]+/backup-rounds/map/.* 105 | pathType: ImplementationSpecific 106 | --- 107 | apiVersion: networking.k8s.io/v1 108 | kind: Ingress 109 | metadata: 110 | name: demos 111 | annotations: 112 | nginx.ingress.kubernetes.io/proxy-body-size: "0" 113 | nginx.ingress.kubernetes.io/client-max-body-size: "0" 114 | nginx.ingress.kubernetes.io/proxy-buffering: "off" 115 | nginx.ingress.kubernetes.io/proxy-request-buffering: "off" 116 | nginx.ingress.kubernetes.io/chunked-transfer-encoding: "false" 117 | nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" 118 | nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" 119 | namespace: 5stack 120 | spec: 121 | ingressClassName: nginx 122 | rules: 123 | - host: $(DEMOS_DOMAIN) 124 | http: 125 | paths: 126 | - backend: 127 | service: 128 | name: api 129 | port: 130 | number: 5585 131 | path: /demos 132 | pathType: Prefix -------------------------------------------------------------------------------- /base/api/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - deployment.yaml 6 | - service.yaml 7 | - ingress.yaml 8 | - ingress-ws.yaml 9 | - rbac/service-accounts.yaml 10 | - rbac/cluster-role-binding.yaml 11 | - rbac/cluster-role.yaml 12 | - rbac/role-binding.yaml 13 | - rbac/role.yaml -------------------------------------------------------------------------------- /base/api/rbac/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: server-creator-clusterrolebinding 5 | subjects: 6 | - kind: ServiceAccount 7 | name: server-creator 8 | namespace: 5stack 9 | roleRef: 10 | kind: ClusterRole 11 | name: server-creator-clusterrole 12 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /base/api/rbac/cluster-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: server-creator-clusterrole 5 | rules: 6 | - apiGroups: 7 | - '' 8 | resources: 9 | - nodes 10 | verbs: 11 | - get 12 | - list 13 | - patch 14 | - apiGroups: 15 | - '' 16 | resources: 17 | - persistentvolumeclaims 18 | verbs: 19 | - create 20 | - get 21 | - list 22 | - watch 23 | - delete 24 | - apiGroups: 25 | - '' 26 | resources: 27 | - persistentvolumes 28 | verbs: 29 | - create 30 | - get 31 | - list 32 | - watch 33 | - delete 34 | - apiGroups: 35 | - batch 36 | resources: 37 | - jobs 38 | - jobs/status 39 | verbs: 40 | - get 41 | - list 42 | - watch 43 | - apiGroups: 44 | - '' 45 | resources: 46 | - pods 47 | - pods/log 48 | verbs: 49 | - get 50 | - list 51 | - watch 52 | - apiGroups: 53 | - apps 54 | resources: 55 | - deployments 56 | verbs: 57 | - patch -------------------------------------------------------------------------------- /base/api/rbac/role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: server-creator-binding 5 | namespace: 5stack 6 | subjects: 7 | - kind: ServiceAccount 8 | name: server-creator 9 | namespace: 5stack 10 | roleRef: 11 | kind: Role 12 | name: server-creator-role 13 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /base/api/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: server-creator-role 5 | namespace: 5stack 6 | rules: 7 | - apiGroups: 8 | - '' 9 | resources: 10 | - pods 11 | verbs: 12 | - create 13 | - get 14 | - list 15 | - watch 16 | - delete 17 | - apiGroups: 18 | - '' 19 | resources: 20 | - services 21 | verbs: 22 | - create 23 | - get 24 | - list 25 | - watch 26 | - delete 27 | - apiGroups: 28 | - '' 29 | resources: 30 | - serviceaccounts 31 | verbs: 32 | - get 33 | - list 34 | - apiGroups: 35 | - '' 36 | resources: 37 | - secrets 38 | verbs: 39 | - get 40 | - list 41 | - apiGroups: 42 | - '' 43 | resources: 44 | - pods/exec 45 | verbs: 46 | - create 47 | - get 48 | - apiGroups: 49 | - 'batch' 50 | resources: 51 | - jobs 52 | verbs: 53 | - create 54 | - get 55 | - list 56 | - watch 57 | - delete 58 | - apiGroups: 59 | - '' 60 | resources: 61 | - nodes 62 | verbs: 63 | - get -------------------------------------------------------------------------------- /base/api/rbac/service-accounts.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: server-creator 5 | namespace: 5stack -------------------------------------------------------------------------------- /base/api/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: api 5 | namespace: 5stack 6 | spec: 7 | ports: 8 | - port: 5585 9 | name: http 10 | protocol: TCP 11 | targetPort: 5585 12 | - port: 5586 13 | name: game-server-node-ws 14 | protocol: TCP 15 | targetPort: 5586 16 | selector: 17 | app: api 18 | -------------------------------------------------------------------------------- /base/game-server-node-connector/daemonset.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | labels: 5 | app: game-server-node-connector 6 | name: game-server-node-connector 7 | namespace: 5stack 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: game-server-node-connector 12 | template: 13 | metadata: 14 | labels: 15 | app: game-server-node-connector 16 | spec: 17 | hostNetwork: true 18 | dnsPolicy: ClusterFirstWithHostNet 19 | serviceAccountName: game-server-node-connector 20 | containers: 21 | - image: ghcr.io/5stackgg/game-server-node:latest 22 | name: game-server-node-connector 23 | env: 24 | - name: NODE_NAME 25 | valueFrom: 26 | fieldRef: 27 | fieldPath: spec.nodeName 28 | envFrom: 29 | - secretRef: 30 | name: hasura-secrets 31 | - secretRef: 32 | name: redis-secrets 33 | volumeMounts: 34 | - name: server-files 35 | mountPath: /serverfiles 36 | - name: demos 37 | mountPath: /demos 38 | volumes: 39 | - name: server-files 40 | hostPath: 41 | path: /opt/5stack/serverfiles 42 | type: Directory 43 | - name: demos 44 | hostPath: 45 | path: /opt/5stack/demos 46 | type: Directory 47 | -------------------------------------------------------------------------------- /base/game-server-node-connector/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - daemonset.yaml 6 | - rbac/cluster-role-binding.yaml 7 | - rbac/cluster-role.yaml 8 | - rbac/role-binding.yaml 9 | - rbac/role.yaml 10 | - rbac/service-accounts.yaml -------------------------------------------------------------------------------- /base/game-server-node-connector/rbac/cluster-role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: game-server-node-connector-clusterrolebinding 5 | subjects: 6 | - kind: ServiceAccount 7 | name: game-server-node-connector 8 | namespace: 5stack 9 | roleRef: 10 | kind: ClusterRole 11 | name: game-server-node-connector-clusterrole 12 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /base/game-server-node-connector/rbac/cluster-role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: game-server-node-connector-clusterrole 5 | rules: 6 | - apiGroups: 7 | - '' 8 | resources: 9 | - nodes 10 | verbs: 11 | - get 12 | - list 13 | - watch 14 | - apiGroups: 15 | - metrics.k8s.io 16 | resources: 17 | - nodes 18 | verbs: 19 | - get 20 | - list 21 | - watch 22 | - apiGroups: 23 | - '' 24 | resources: 25 | - pods 26 | verbs: 27 | - get 28 | - list 29 | - watch 30 | - apiGroups: 31 | - metrics.k8s.io 32 | resources: 33 | - pods 34 | verbs: 35 | - get 36 | - list 37 | - watch -------------------------------------------------------------------------------- /base/game-server-node-connector/rbac/role-binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: game-server-node-connector-rolebinding 5 | namespace: 5stack 6 | subjects: 7 | - kind: ServiceAccount 8 | name: game-server-node-connector 9 | namespace: 5stack 10 | roleRef: 11 | kind: Role 12 | name: game-server-node-connector-role 13 | apiGroup: rbac.authorization.k8s.io -------------------------------------------------------------------------------- /base/game-server-node-connector/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: Role 3 | metadata: 4 | name: game-server-node-connector-role 5 | namespace: 5stack 6 | rules: 7 | - apiGroups: 8 | - '' 9 | resources: 10 | - pods 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - '' 17 | resources: 18 | - pods/exec 19 | verbs: 20 | - create -------------------------------------------------------------------------------- /base/game-server-node-connector/rbac/service-accounts.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: game-server-node-connector 5 | namespace: 5stack -------------------------------------------------------------------------------- /base/game-server-node-connector/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: game-server-node-connector 5 | namespace: 5stack 6 | spec: 7 | type: NodePort 8 | selector: 9 | app: game-server-node-connector 10 | ports: 11 | - protocol: TCP 12 | port: 80 13 | targetPort: 80 14 | nodePort: 30085 -------------------------------------------------------------------------------- /base/hasura/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: hasura 6 | name: hasura 7 | namespace: 5stack 8 | spec: 9 | replicas: 1 10 | strategy: 11 | type: RollingUpdate 12 | rollingUpdate: 13 | maxUnavailable: 0 14 | maxSurge: 1 15 | selector: 16 | matchLabels: 17 | app: hasura 18 | template: 19 | metadata: 20 | labels: 21 | app: hasura 22 | spec: 23 | affinity: 24 | nodeAffinity: 25 | requiredDuringSchedulingIgnoredDuringExecution: 26 | nodeSelectorTerms: 27 | - matchExpressions: 28 | - key: 5stack-hasura 29 | operator: In 30 | values: 31 | - "true" 32 | containers: 33 | - image: hasura/graphql-engine:v2.47.0-ce.cli-migrations-v3 34 | name: hasura 35 | ports: 36 | - containerPort: 8080 37 | envFrom: 38 | - secretRef: 39 | name: hasura-secrets 40 | env: 41 | - name: HASURA_GRAPHQL_CONFIGURED_HEADER_PRECEDENCE 42 | value: "true" 43 | - name: HASURA_GRAPHQL_DEV_MODE 44 | value: "true" 45 | - name: HASURA_GRAPHQL_ENABLE_CONSOLE 46 | value: "true" 47 | - name: HASURA_GRAPHQL_STRINGIFY_NUMERIC_TYPES 48 | value: "true" 49 | - name: HASURA_GRAPHQL_AUTH_HOOK 50 | value: "http://api:5585/hasura" 51 | - name: HASURA_GRAPHQL_EVENT_HOOK 52 | value: "http://api:5585/hasura/events" 53 | - name: HASURA_GRAPHQL_ACTIONS_HOOK 54 | value: "http://api:5585/hasura/actions" 55 | - name: HASURA_GRAPHQL_LOG_LEVEL 56 | value: "warn" 57 | - name: HASURA_GRAPHQL_ENABLE_TELEMETRY 58 | value: "false" 59 | - name: HASURA_GRAPHQL_METADATA_DIR 60 | value: /app/hasura/metadata 61 | - name: HASURA_GRAPHQL_DATABASE_URL 62 | valueFrom: 63 | secretKeyRef: 64 | name: timescaledb-secrets 65 | key: POSTGRES_CONNECTION_STRING 66 | volumeMounts: 67 | - mountPath: /app 68 | name: workdir 69 | initContainers: 70 | - name: migrations 71 | image: ghcr.io/5stackgg/api:latest 72 | command: 73 | - /bin/sh 74 | - -c 75 | - | 76 | cp -r /opt/5stack/hasura /app && 77 | node dist/src/main.js 78 | envFrom: 79 | - configMapRef: 80 | name: api-config 81 | - secretRef: 82 | name: api-secrets 83 | - secretRef: 84 | name: steam-secrets 85 | - secretRef: 86 | name: hasura-secrets 87 | - secretRef: 88 | name: typesense-secrets 89 | - secretRef: 90 | name: discord-secrets 91 | - secretRef: 92 | name: redis-secrets 93 | - secretRef: 94 | name: timescaledb-secrets 95 | - configMapRef: 96 | name: s3-config 97 | env: 98 | - name: RUN_MIGRATIONS 99 | value: "true" 100 | volumeMounts: 101 | - mountPath: /app 102 | name: workdir 103 | volumes: 104 | - emptyDir: {} 105 | name: workdir 106 | - name: timescaledb-secrets 107 | secret: 108 | secretName: timescaledb-secrets -------------------------------------------------------------------------------- /base/hasura/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - deployment.yaml 6 | - service.yaml -------------------------------------------------------------------------------- /base/hasura/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: hasura 5 | namespace: 5stack 6 | spec: 7 | ports: 8 | - port: 8080 9 | protocol: TCP 10 | targetPort: 8080 11 | selector: 12 | app: hasura 13 | -------------------------------------------------------------------------------- /base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - namespace.yaml 6 | - api 7 | - game-server-node-connector 8 | - hasura 9 | - minio 10 | - timescaledb 11 | - redis 12 | - typesense 13 | - web 14 | - volumes 15 | - nginx 16 | 17 | 18 | configMapGenerator: 19 | - name: api-config 20 | namespace: 5stack 21 | envs: 22 | - properties/api-config.env 23 | - name: typesense-config 24 | namespace: 5stack 25 | envs: 26 | - properties/typesense-config.env 27 | behavior: merge 28 | - name: s3-config 29 | namespace: 5stack 30 | envs: 31 | - properties/s3-config.env 32 | - name: nginx-config 33 | namespace: 5stack 34 | envs: 35 | - properties/nginx-config.env 36 | 37 | replacements: 38 | - source: 39 | kind: ConfigMap 40 | name: api-config 41 | fieldPath: data.WS_DOMAIN 42 | targets: 43 | - select: 44 | kind: Ingress 45 | name: ws 46 | fieldPaths: 47 | - spec.rules.0.host 48 | - source: 49 | kind: ConfigMap 50 | name: api-config 51 | fieldPath: data.API_DOMAIN 52 | targets: 53 | - select: 54 | kind: Ingress 55 | name: api 56 | fieldPaths: 57 | - spec.rules.0.host 58 | - source: 59 | kind: ConfigMap 60 | name: api-config 61 | fieldPath: data.DEMOS_DOMAIN 62 | targets: 63 | - select: 64 | kind: Ingress 65 | name: demos 66 | fieldPaths: 67 | - spec.rules.0.host 68 | - source: 69 | kind: ConfigMap 70 | name: api-config 71 | fieldPath: data.WEB_DOMAIN 72 | targets: 73 | - select: 74 | kind: Ingress 75 | name: api 76 | fieldPaths: 77 | - spec.rules.1.host 78 | - select: 79 | kind: Ingress 80 | name: web 81 | fieldPaths: 82 | - spec.rules.0.host 83 | - source: 84 | kind: ConfigMap 85 | name: s3-config 86 | fieldPath: data.S3_CONSOLE_HOST 87 | targets: 88 | - select: 89 | kind: Ingress 90 | name: s3-console 91 | fieldPaths: 92 | - spec.rules.0.host 93 | - source: 94 | kind: ConfigMap 95 | name: typesense-config 96 | fieldPath: data.TYPESENSE_HOST 97 | targets: 98 | - select: 99 | kind: Ingress 100 | name: typesense 101 | fieldPaths: 102 | - spec.rules.0.host 103 | - source: 104 | kind: ConfigMap 105 | name: nginx-config 106 | fieldPath: data.HTTP_PORT 107 | targets: 108 | - select: 109 | kind: Service 110 | name: ingress-nginx-controller 111 | namespace: ingress-nginx 112 | fieldPaths: 113 | - spec.ports.0.port 114 | - source: 115 | kind: ConfigMap 116 | name: nginx-config 117 | fieldPath: data.HTTPS_PORT 118 | targets: 119 | - select: 120 | kind: Service 121 | name: ingress-nginx-controller 122 | namespace: ingress-nginx 123 | fieldPaths: 124 | - spec.ports.1.port 125 | 126 | 127 | secretGenerator: 128 | - name: api-secrets 129 | type: Opaque 130 | namespace: 5stack 131 | envs: 132 | - secrets/api-secrets.env 133 | 134 | - name: timescaledb-secrets 135 | type: Opaque 136 | namespace: 5stack 137 | envs: 138 | - secrets/timescaledb-secrets.env 139 | - name: tailscale-secrets 140 | type: Opaque 141 | namespace: 5stack 142 | envs: 143 | - secrets/tailscale-secrets.env 144 | - name: steam-secrets 145 | type: Opaque 146 | namespace: 5stack 147 | envs: 148 | - secrets/steam-secrets.env 149 | - name: hasura-secrets 150 | type: Opaque 151 | namespace: 5stack 152 | envs: 153 | - secrets/hasura-secrets.env 154 | - name: discord-secrets 155 | type: Opaque 156 | namespace: 5stack 157 | envs: 158 | - secrets/discord-secrets.env 159 | - name: s3-secrets 160 | type: Opaque 161 | namespace: 5stack 162 | envs: 163 | - secrets/s3-secrets.env 164 | - name: redis-secrets 165 | type: Opaque 166 | namespace: 5stack 167 | envs: 168 | - secrets/redis-secrets.env 169 | - name: typesense-secrets 170 | type: Opaque 171 | namespace: 5stack 172 | envs: 173 | - secrets/typesense-secrets.env 174 | - name: minio-secrets 175 | type: Opaque 176 | namespace: 5stack 177 | envs: 178 | - secrets/minio-secrets.env 179 | - name: faceit-secrets 180 | type: Opaque 181 | namespace: 5stack 182 | envs: 183 | - secrets/faceit-secrets.env 184 | 185 | # TODO - disable dev mode for hasura in prod 186 | # HASURA_GRAPHQL_DEV_MODE -------------------------------------------------------------------------------- /base/minio/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | annotations: 5 | nginx.ingress.kubernetes.io/rewrite-target: /$1 6 | nginx.ingress.kubernetes.io/proxy-body-size: 2048m 7 | name: s3-console 8 | namespace: 5stack 9 | spec: 10 | ingressClassName: nginx 11 | rules: 12 | - host: ${S3_CONSOLE_HOST} 13 | http: 14 | paths: 15 | - backend: 16 | service: 17 | name: minio 18 | port: 19 | number: 9090 20 | path: /(.*) 21 | pathType: ImplementationSpecific 22 | -------------------------------------------------------------------------------- /base/minio/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - stateful-set.yaml 6 | - service.yaml 7 | - ingress.yaml -------------------------------------------------------------------------------- /base/minio/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: minio 5 | namespace: 5stack 6 | spec: 7 | ports: 8 | - port: 9000 9 | name: api 10 | targetPort: 9000 11 | protocol: TCP 12 | - port: 9090 13 | name: console 14 | targetPort: 9090 15 | protocol: TCP 16 | selector: 17 | app: minio -------------------------------------------------------------------------------- /base/minio/stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: minio 5 | labels: 6 | app: minio 7 | namespace: 5stack 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: minio 13 | template: 14 | metadata: 15 | labels: 16 | app: minio 17 | spec: 18 | affinity: 19 | nodeAffinity: 20 | requiredDuringSchedulingIgnoredDuringExecution: 21 | nodeSelectorTerms: 22 | - matchExpressions: 23 | - key: 5stack-minio 24 | operator: In 25 | values: 26 | - "true" 27 | containers: 28 | - name: minio 29 | image: quay.io/minio/minio:latest 30 | ports: 31 | - containerPort: 9000 32 | - containerPort: 9090 33 | envFrom: 34 | - configMapRef: 35 | name: s3-config 36 | - secretRef: 37 | name: minio-secrets 38 | - secretRef: 39 | name: s3-secrets 40 | command: 41 | - /bin/bash 42 | - -c 43 | args: 44 | - | 45 | mkdir -p /data/5stack && 46 | minio server /data --console-address :9090 & 47 | sleep 10 && 48 | mc alias set myminio http://localhost:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD} && 49 | mc mb myminio/5stack || true 50 | mc admin user add myminio ${S3_ACCESS_KEY} ${S3_SECRET} && 51 | mc admin policy attach myminio readwrite --user=${S3_ACCESS_KEY} 52 | wait 53 | volumeMounts: 54 | - name: minio-data 55 | mountPath: /data 56 | volumes: 57 | - name: minio-data 58 | persistentVolumeClaim: 59 | claimName: minio-pvc -------------------------------------------------------------------------------- /base/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: 5stack -------------------------------------------------------------------------------- /base/nginx/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - service.yaml -------------------------------------------------------------------------------- /base/nginx/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: controller 6 | app.kubernetes.io/instance: ingress-nginx 7 | app.kubernetes.io/name: ingress-nginx 8 | app.kubernetes.io/part-of: ingress-nginx 9 | app.kubernetes.io/version: 1.12.1 10 | name: ingress-nginx-controller 11 | namespace: ingress-nginx 12 | spec: 13 | externalTrafficPolicy: Local 14 | ipFamilies: 15 | - IPv4 16 | ipFamilyPolicy: SingleStack 17 | ports: 18 | - appProtocol: http 19 | name: http 20 | port: 80 21 | protocol: TCP 22 | targetPort: http 23 | - appProtocol: https 24 | name: https 25 | port: 443 26 | protocol: TCP 27 | targetPort: https 28 | selector: 29 | app.kubernetes.io/component: controller 30 | app.kubernetes.io/instance: ingress-nginx 31 | app.kubernetes.io/name: ingress-nginx 32 | type: LoadBalancer 33 | -------------------------------------------------------------------------------- /base/properties/api-config.env.example: -------------------------------------------------------------------------------- 1 | # Each domain must be unique for routing purposes 2 | 3 | # example: 5stack.gg 4 | WEB_DOMAIN= 5 | 6 | # example: ws.5stack.gg 7 | WS_DOMAIN= 8 | 9 | # example: api.5stack.gg 10 | API_DOMAIN= 11 | 12 | # example: demos.5stack.gg 13 | DEMOS_DOMAIN= 14 | 15 | # example: hello@example.com 16 | MAIL_FROM= 17 | 18 | # after installing tailscale get the ip 19 | TAILSCALE_NODE_IP= 20 | 21 | # https://login.tailscale.com/admin/dns 22 | TAILSCALE_NET_NAME= 23 | 24 | # create tailscale oauth client 25 | # https://login.tailscale.com/admin/settings/oauth 26 | # give the `devices` scope 27 | TAILSCALE_CLIENT_ID= 28 | 29 | #S3_PORT=443 30 | #S3_USE_SSL=true 31 | #S3_ENDPOINT=s3.us-east-005.backblazeb2.com 32 | -------------------------------------------------------------------------------- /base/properties/nginx-config.env.example: -------------------------------------------------------------------------------- 1 | HTTP_PORT=80 2 | HTTPS_PORT=443 3 | -------------------------------------------------------------------------------- /base/properties/s3-config.env.example: -------------------------------------------------------------------------------- 1 | S3_BUCKET=5stack 2 | 3 | # example: console.example.com 4 | S3_CONSOLE_HOST= 5 | 6 | S3_ENDPOINT= 7 | S3_USE_SSL=true 8 | S3_PORT=443 -------------------------------------------------------------------------------- /base/properties/typesense-config.env.example: -------------------------------------------------------------------------------- 1 | # example: search.example.com 2 | TYPESENSE_HOST= -------------------------------------------------------------------------------- /base/redis/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: redis-config 5 | namespace: 5stack 6 | data: 7 | redis.conf: | 8 | tcp-keepalive 240 9 | appendonly no 10 | save "" 11 | maxmemory-policy noeviction 12 | requirepass ${REDIS_PASSWORD} 13 | -------------------------------------------------------------------------------- /base/redis/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: redis 5 | namespace: 5stack 6 | spec: 7 | replicas: 1 8 | strategy: 9 | type: RollingUpdate 10 | rollingUpdate: 11 | maxUnavailable: 0 12 | maxSurge: 1 13 | selector: 14 | matchLabels: 15 | app: redis 16 | template: 17 | metadata: 18 | labels: 19 | app: redis 20 | spec: 21 | affinity: 22 | nodeAffinity: 23 | requiredDuringSchedulingIgnoredDuringExecution: 24 | nodeSelectorTerms: 25 | - matchExpressions: 26 | - key: 5stack-redis 27 | operator: In 28 | values: 29 | - "true" 30 | containers: 31 | - name: redis 32 | image: redis:7.4-alpine 33 | ports: 34 | - containerPort: 6379 35 | protocol: TCP 36 | envFrom: 37 | - secretRef: 38 | name: redis-secrets 39 | command: 40 | - sh 41 | - -c 42 | - | 43 | sed -e "s/\${REDIS_PASSWORD}/$REDIS_PASSWORD/g" /redis-config/redis.conf > /tmp/redis.conf && redis-server /tmp/redis.conf 44 | volumeMounts: 45 | - name: redis-config 46 | mountPath: /redis-config 47 | volumes: 48 | - name: redis-config 49 | configMap: 50 | name: redis-config 51 | 52 | -------------------------------------------------------------------------------- /base/redis/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - deployment.yaml 6 | - service.yaml 7 | - configmap.yaml -------------------------------------------------------------------------------- /base/redis/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: redis 5 | namespace: 5stack 6 | spec: 7 | ports: 8 | - name: tcp-port 9 | port: 6379 10 | protocol: TCP 11 | targetPort: 6379 12 | selector: 13 | app: redis 14 | -------------------------------------------------------------------------------- /base/secrets/api-secrets.env.example: -------------------------------------------------------------------------------- 1 | APP_KEY=$(RAND32) 2 | ENC_SECRET=$(RAND32) 3 | K3S_TOKEN= -------------------------------------------------------------------------------- /base/secrets/discord-secrets.env.example: -------------------------------------------------------------------------------- 1 | DISCORD_BOT_TOKEN= 2 | DISCORD_CLIENT_ID= 3 | DISCORD_CLIENT_SECRET= -------------------------------------------------------------------------------- /base/secrets/faceit-secrets.env.example: -------------------------------------------------------------------------------- 1 | FACEIT_API_KEY= -------------------------------------------------------------------------------- /base/secrets/hasura-secrets.env.example: -------------------------------------------------------------------------------- 1 | HASURA_GRAPHQL_ADMIN_SECRET=$(RAND32) 2 | 3 | 4 | -------------------------------------------------------------------------------- /base/secrets/minio-secrets.env.example: -------------------------------------------------------------------------------- 1 | MINIO_ROOT_USER=5stack 2 | MINIO_ROOT_PASSWORD=$(RAND32) -------------------------------------------------------------------------------- /base/secrets/redis-secrets.env.example: -------------------------------------------------------------------------------- 1 | REDIS_PASSWORD=$(RAND32) -------------------------------------------------------------------------------- /base/secrets/s3-secrets.env.example: -------------------------------------------------------------------------------- 1 | # access your s3 bucket at https://console...... , create a bucket and access key 2 | S3_ACCESS_KEY=$(RAND32) 3 | S3_SECRET=$(RAND32) -------------------------------------------------------------------------------- /base/secrets/steam-secrets.env.example: -------------------------------------------------------------------------------- 1 | # https://steamcommunity.com/dev/apikey 2 | STEAM_WEB_API_KEY= -------------------------------------------------------------------------------- /base/secrets/tailscale-secrets.env.example: -------------------------------------------------------------------------------- 1 | TAILSCALE_SECRET_ID= -------------------------------------------------------------------------------- /base/secrets/timescaledb-secrets.env.example: -------------------------------------------------------------------------------- 1 | POSTGRES_PASSWORD=$(RAND32) -------------------------------------------------------------------------------- /base/secrets/typesense-secrets.env.example: -------------------------------------------------------------------------------- 1 | TYPESENSE_API_KEY=$(RAND32) -------------------------------------------------------------------------------- /base/timescaledb/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - stateful-set.yaml 6 | - service.yaml -------------------------------------------------------------------------------- /base/timescaledb/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: timescaledb 5 | namespace: 5stack 6 | spec: 7 | ports: 8 | - port: 5432 9 | protocol: TCP 10 | targetPort: 5432 11 | selector: 12 | app: timescaledb 13 | -------------------------------------------------------------------------------- /base/timescaledb/stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: timescaledb 5 | namespace: 5stack 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: timescaledb 11 | template: 12 | metadata: 13 | labels: 14 | app: timescaledb 15 | spec: 16 | affinity: 17 | nodeAffinity: 18 | requiredDuringSchedulingIgnoredDuringExecution: 19 | nodeSelectorTerms: 20 | - matchExpressions: 21 | - key: 5stack-timescaledb 22 | operator: In 23 | values: 24 | - 'true' 25 | containers: 26 | - name: timescaledb 27 | image: timescale/timescaledb:latest-pg17 28 | args: 29 | - postgres 30 | - '-c' 31 | - fivestack.app_key=$(APP_KEY) 32 | ports: 33 | - containerPort: 5432 34 | protocol: TCP 35 | envFrom: 36 | - secretRef: 37 | name: timescaledb-secrets 38 | env: 39 | - name: POSTGRES_DB 40 | value: hasura 41 | - name: POSTGRES_USER 42 | value: hasura 43 | - name: TIMESCALEDB_TELEMETRY 44 | value: "off" 45 | - name: APP_KEY 46 | valueFrom: 47 | secretKeyRef: 48 | name: api-secrets 49 | key: APP_KEY 50 | volumeMounts: 51 | - name: api-secrets 52 | mountPath: /etc/api-secrets 53 | readOnly: true 54 | - name: timescaledb-data 55 | mountPath: /var/lib/postgresql/data 56 | subPath: timescaledb 57 | volumes: 58 | - name: api-secrets 59 | secret: 60 | secretName: api-secrets 61 | - name: timescaledb-data 62 | persistentVolumeClaim: 63 | claimName: timescaledb-pvc -------------------------------------------------------------------------------- /base/typesense/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: typesense-web-config 5 | namespace: 5stack 6 | data: 7 | config.json: | 8 | { 9 | "apiKey": "$(TYPESENSE_API_KEY)", 10 | "node": { 11 | "host": $(TYPESENSE_HOST), 12 | "port":"443", 13 | "protocol":"https", 14 | "path":"", 15 | "tls": true 16 | } 17 | } 18 | --- 19 | apiVersion: v1 20 | kind: ConfigMap 21 | metadata: 22 | name: typesense-config 23 | namespace: 5stack 24 | data: 25 | TYPESENSE_DATA_DIR: /data -------------------------------------------------------------------------------- /base/typesense/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: typesense 5 | namespace: 5stack 6 | spec: 7 | ingressClassName: nginx 8 | rules: 9 | - host: $(TYPESENSE_HOST) 10 | http: 11 | paths: 12 | - backend: 13 | service: 14 | name: typesense 15 | port: 16 | number: 8108 17 | path: / 18 | pathType: Prefix -------------------------------------------------------------------------------- /base/typesense/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - stateful-set.yaml 6 | - service.yaml 7 | - ingress.yaml 8 | - configmap.yaml -------------------------------------------------------------------------------- /base/typesense/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: typesense 5 | namespace: 5stack 6 | spec: 7 | ports: 8 | - port: 8108 9 | targetPort: 8108 10 | protocol: TCP 11 | selector: 12 | app: typesense 13 | -------------------------------------------------------------------------------- /base/typesense/stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | labels: 5 | app: typesense 6 | name: typesense 7 | namespace: 5stack 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: typesense 13 | template: 14 | metadata: 15 | labels: 16 | app: typesense 17 | spec: 18 | affinity: 19 | nodeAffinity: 20 | requiredDuringSchedulingIgnoredDuringExecution: 21 | nodeSelectorTerms: 22 | - matchExpressions: 23 | - key: 5stack-typesense 24 | operator: In 25 | values: 26 | - "true" 27 | containers: 28 | - name: typesense 29 | image: typesense/typesense:28.0 30 | ports: 31 | - containerPort: 8108 32 | volumeMounts: 33 | - name: typesense-config 34 | mountPath: /etc/typesense 35 | - name: typesense-data 36 | mountPath: /data 37 | envFrom: 38 | - configMapRef: 39 | name: typesense-config 40 | - secretRef: 41 | name: typesense-secrets 42 | command: 43 | - /opt/typesense-server 44 | - --enable-cors 45 | - --cors-domains=http://localhost,https://$(TYPESENSE_HOST) 46 | - name: typesense-web 47 | image: ghcr.io/bfritscher/typesense-dashboard:2.0.4 48 | ports: 49 | - containerPort: 80 50 | volumeMounts: 51 | - mountPath: /srv/config.json 52 | name: web-config 53 | subPath: config.json 54 | volumes: 55 | - name: web-config 56 | configMap: 57 | name: typesense-web-config 58 | - name: typesense-config 59 | configMap: 60 | name: typesense-config 61 | - name: typesense-data 62 | persistentVolumeClaim: 63 | claimName: typesense-pvc -------------------------------------------------------------------------------- /base/volumes/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - minio-pv.yaml 6 | - typesense-pv.yaml 7 | - timescaledb-pv.yaml -------------------------------------------------------------------------------- /base/volumes/minio-pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: minio-pv 5 | namespace: 5stack 6 | spec: 7 | capacity: 8 | storage: 2Gi 9 | accessModes: 10 | - ReadWriteOnce 11 | persistentVolumeReclaimPolicy: Retain 12 | storageClassName: local-storage 13 | local: 14 | path: /opt/5stack/minio 15 | nodeAffinity: 16 | required: 17 | nodeSelectorTerms: 18 | - matchExpressions: 19 | - key: 5stack-minio 20 | operator: In 21 | values: 22 | - "true" 23 | --- 24 | apiVersion: v1 25 | kind: PersistentVolumeClaim 26 | metadata: 27 | name: minio-pvc 28 | namespace: 5stack 29 | spec: 30 | accessModes: 31 | - ReadWriteOnce 32 | storageClassName: local-storage 33 | resources: 34 | requests: 35 | storage: 2Gi 36 | volumeName: minio-pv -------------------------------------------------------------------------------- /base/volumes/timescaledb-pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: timescaledb-pv 5 | namespace: 5stack 6 | spec: 7 | capacity: 8 | storage: 10Gi 9 | accessModes: 10 | - ReadWriteOnce 11 | persistentVolumeReclaimPolicy: Retain 12 | storageClassName: local-storage 13 | local: 14 | path: /opt/5stack/timescaledb 15 | nodeAffinity: 16 | required: 17 | nodeSelectorTerms: 18 | - matchExpressions: 19 | - key: 5stack-timescaledb 20 | operator: In 21 | values: 22 | - "true" 23 | --- 24 | apiVersion: v1 25 | kind: PersistentVolumeClaim 26 | metadata: 27 | name: timescaledb-pvc 28 | namespace: 5stack 29 | spec: 30 | accessModes: 31 | - ReadWriteOnce 32 | storageClassName: local-storage 33 | resources: 34 | requests: 35 | storage: 10Gi 36 | volumeName: timescaledb-pv -------------------------------------------------------------------------------- /base/volumes/typesense-pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: typesense-pv 5 | namespace: 5stack 6 | spec: 7 | capacity: 8 | storage: 5Gi 9 | accessModes: 10 | - ReadWriteOnce 11 | persistentVolumeReclaimPolicy: Retain 12 | storageClassName: local-storage 13 | local: 14 | path: /opt/5stack/typesense 15 | nodeAffinity: 16 | required: 17 | nodeSelectorTerms: 18 | - matchExpressions: 19 | - key: 5stack-typesense 20 | operator: In 21 | values: 22 | - "true" 23 | --- 24 | apiVersion: v1 25 | kind: PersistentVolumeClaim 26 | metadata: 27 | name: typesense-pvc 28 | namespace: 5stack 29 | spec: 30 | accessModes: 31 | - ReadWriteOnce 32 | storageClassName: local-storage 33 | resources: 34 | requests: 35 | storage: 5Gi 36 | volumeName: typesense-pv -------------------------------------------------------------------------------- /base/web/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: web 6 | name: web 7 | namespace: 5stack 8 | spec: 9 | replicas: 1 10 | strategy: 11 | type: RollingUpdate 12 | rollingUpdate: 13 | maxUnavailable: 0 14 | maxSurge: 1 15 | selector: 16 | matchLabels: 17 | app: web 18 | template: 19 | metadata: 20 | labels: 21 | app: web 22 | spec: 23 | affinity: 24 | nodeAffinity: 25 | requiredDuringSchedulingIgnoredDuringExecution: 26 | nodeSelectorTerms: 27 | - matchExpressions: 28 | - key: 5stack-web 29 | operator: In 30 | values: 31 | - 'true' 32 | containers: 33 | - image: ghcr.io/5stackgg/web:latest 34 | name: web 35 | ports: 36 | - containerPort: 3000 37 | env: 38 | - name: NUXT_PUBLIC_API_DOMAIN 39 | valueFrom: 40 | configMapKeyRef: 41 | name: api-config 42 | key: API_DOMAIN 43 | - name: NUXT_PUBLIC_WEB_DOMAIN 44 | valueFrom: 45 | configMapKeyRef: 46 | name: api-config 47 | key: WEB_DOMAIN 48 | - name: NUXT_PUBLIC_DEMOS_DOMAIN 49 | valueFrom: 50 | configMapKeyRef: 51 | name: api-config 52 | key: DEMOS_DOMAIN 53 | - name: NUXT_PUBLIC_WS_DOMAIN 54 | valueFrom: 55 | configMapKeyRef: 56 | name: api-config 57 | key: WS_DOMAIN 58 | - name: NUXT_PUBLIC_TYPESENSE_HOST 59 | valueFrom: 60 | configMapKeyRef: 61 | name: typesense-config 62 | key: TYPESENSE_HOST 63 | - name: HASURA_GRAPHQL_ADMIN_SECRET 64 | valueFrom: 65 | secretKeyRef: 66 | name: hasura-secrets 67 | key: HASURA_GRAPHQL_ADMIN_SECRET 68 | - name: TYPESENSE_API_KEY 69 | valueFrom: 70 | secretKeyRef: 71 | name: typesense-secrets 72 | key: TYPESENSE_API_KEY 73 | - name: STEAM_API_KEY 74 | valueFrom: 75 | secretKeyRef: 76 | name: steam-secrets 77 | key: STEAM_WEB_API_KEY 78 | -------------------------------------------------------------------------------- /base/web/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | annotations: 5 | nginx.ingress.kubernetes.io/rewrite-target: /$1 6 | name: web 7 | namespace: 5stack 8 | spec: 9 | ingressClassName: nginx 10 | rules: 11 | - host: ${WEB_DOMAIN} 12 | http: 13 | paths: 14 | - backend: 15 | service: 16 | name: web 17 | port: 18 | number: 3000 19 | path: /(.*) 20 | pathType: ImplementationSpecific -------------------------------------------------------------------------------- /base/web/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - deployment.yaml 6 | - service.yaml 7 | - ingress.yaml -------------------------------------------------------------------------------- /base/web/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: web 5 | namespace: 5stack 6 | spec: 7 | ports: 8 | - port: 3000 9 | protocol: TCP 10 | targetPort: 3000 11 | selector: 12 | app: web 13 | -------------------------------------------------------------------------------- /custom.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | KUBECONFIG="" 4 | CUSTOM_DIR="" 5 | 6 | while [[ $# -gt 0 ]]; do 7 | case $1 in 8 | --kubeconfig) 9 | KUBECONFIG="$2" 10 | shift 2 11 | ;; 12 | *) 13 | CUSTOM_DIR="$1" 14 | shift 15 | ;; 16 | esac 17 | done 18 | 19 | if [ -z "$CUSTOM_DIR" ]; then 20 | echo "Error: CUSTOM_DIR is required." 21 | exit 1 22 | fi 23 | 24 | if [ -z "$KUBECONFIG" ]; then 25 | KUBECONFIG="/etc/rancher/k3s/k3s.yaml" 26 | fi 27 | 28 | ./kustomize build ./custom/$CUSTOM_DIR | kubectl --kubeconfig=$KUBECONFIG apply -f - -------------------------------------------------------------------------------- /debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source setup-env.sh "$@" 4 | 5 | namespace="5stack" 6 | debug_file="debug_output_$(date +%Y%m%d_%H%M%S).txt" 7 | 8 | echo "Checking pod status and restarts in namespace $namespace..." | tee -a "$debug_file" 9 | echo "---------------------------------------" | tee -a "$debug_file" 10 | 11 | # Get all pods in the namespace 12 | kubectl --kubeconfig=$KUBECONFIG get pods -n "$namespace" --no-headers | while read -r pod; do 13 | pod_name=$(echo "$pod" | awk '{print $1}') 14 | restarts=$(echo "$pod" | awk '{print $4}') 15 | 16 | echo "Pod: $pod_name" | tee -a "$debug_file" 17 | echo "Total restarts: $restarts" | tee -a "$debug_file" 18 | 19 | echo "Last restart reason:" | tee -a "$debug_file" 20 | kubectl --kubeconfig=$KUBECONFIG get pod "$pod_name" -n "$namespace" -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}' | tee -a "$debug_file" 21 | echo -e "\n" | tee -a "$debug_file" 22 | 23 | kubectl --kubeconfig=$KUBECONFIG logs "$pod_name" -n "$namespace" --tail=100 | tee -a "$debug_file" 24 | 25 | echo "---------------------------------------" | tee -a "$debug_file" 26 | done 27 | 28 | echo "Checking certificates in namespace $namespace..." | tee -a "$debug_file" 29 | echo "---------------------------------------" | tee -a "$debug_file" 30 | 31 | # Loop through all certificates in all namespaces 32 | for cert in $(kubectl --kubeconfig=$KUBECONFIG get certificates -A -o custom-columns=NAME:.metadata.name --no-headers | awk '{print $1}'); do 33 | name=$(echo "$cert" | cut -d'/' -f2) 34 | 35 | # Get the Ready condition status for the certificate 36 | status=$(kubectl --kubeconfig=$KUBECONFIG get certificate "$name" -n "$namespace" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}') 37 | 38 | # If the certificate is valid, skip to the next 39 | if [[ "$status" == "True" ]]; then 40 | echo "Certificate $name in namespace $namespace is valid." | tee -a "$debug_file" 41 | else 42 | echo "Certificate $name in namespace $namespace is not valid. Fetching details..." | tee -a "$debug_file" 43 | 44 | # Get details of the certificate 45 | kubectl --kubeconfig=$KUBECONFIG describe certificate "$name" -n "$namespace" | tee -a "$debug_file" 46 | 47 | # Find associated challenges for this certificate 48 | echo "Checking challenges for certificate $name..." | tee -a "$debug_file" 49 | 50 | # Get all challenges in the same namespace and filter by the certificate name 51 | kubectl --kubeconfig=$KUBECONFIG get challenges -n "$namespace" --no-headers | grep "$name" | while read -r challenge; do 52 | challenge_name=$(echo "$challenge" | awk '{print $1}') 53 | 54 | # Check if the challenge is valid 55 | challenge_status=$(kubectl --kubeconfig=$KUBECONFIG get challenge "$challenge_name" -n "$namespace" -o jsonpath='{.status.state}') 56 | 57 | # Only fetch details if the challenge is not valid 58 | if [[ "$challenge_status" != "valid" ]]; then 59 | echo "Challenge $challenge_name for certificate $name is not valid. Fetching details..." | tee -a "$debug_file" 60 | kubectl --kubeconfig=$KUBECONFIG describe challenge "$challenge_name" -n "$namespace" | tee -a "$debug_file" 61 | else 62 | echo "Challenge $challenge_name for certificate $name is valid." | tee -a "$debug_file" 63 | fi 64 | done 65 | fi 66 | 67 | echo "---------------------------------------" | tee -a "$debug_file" 68 | done 69 | 70 | echo -e "\033[32mDebug log saved to: $debug_file\033[0m" 71 | -------------------------------------------------------------------------------- /dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source setup-env.sh "$@" 4 | 5 | ./kustomize build overlays/dev | kubectl --kubeconfig=$KUBECONFIG apply -f - 6 | ./kustomize build overlays/nvidia | kubectl --kubeconfig=$KUBECONFIG apply -f - -------------------------------------------------------------------------------- /fix-ssl.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source setup-env.sh "$@" 4 | 5 | kubectl --kubeconfig=$KUBECONFIG delete ingresses --all -n 5stack 6 | 7 | source update.sh "$@" 8 | -------------------------------------------------------------------------------- /fix-versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | crictl image | grep ghcr.io/5stackgg | awk '{print $3}' | xargs -n 1 crictl rmi -------------------------------------------------------------------------------- /game-node-server-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source setup-env.sh "$@" 4 | 5 | echo "Installing Game Node Server dependencies..." 6 | 7 | curl -sfL https://tailscale.com/install.sh | sh 8 | 9 | echo "Generate and enter your Tailscale auth key: https://login.tailscale.com/admin/settings/keys, make sure to select the \"Pre Approved\" option" 10 | 11 | echo -e "\033[1;36mEnter your Tailscale auth key:\033[0m" 12 | read TAILSCALE_AUTH_KEY 13 | 14 | while [ -z "$TAILSCALE_AUTH_KEY" ]; do 15 | echo "Tailscale auth key cannot be empty. Please enter your Tailscale auth key:" 16 | read TAILSCALE_AUTH_KEY 17 | done 18 | 19 | curl -sfL https://get.k3s.io | sh -s - --disable=traefik --vpn-auth="name=tailscale,joinKey=${TAILSCALE_AUTH_KEY}"; 20 | 21 | 22 | echo -e "\033[1;36mEnter your Tailscale network name (e.g. example.ts.net) from https://login.tailscale.com/admin/dns:\033[0m" 23 | read TAILSCALE_NET_NAME 24 | while [ -z "$TAILSCALE_NET_NAME" ]; do 25 | echo "Tailscale network name cannot be empty. Please enter your Tailscale network name (e.g. example.ts.net):" 26 | read TAILSCALE_NET_NAME 27 | done 28 | 29 | update_env_var "base/properties/api-config.env" "TAILSCALE_NET_NAME" "$TAILSCALE_NET_NAME" 30 | 31 | 32 | echo -e "\033[1;31mCreate an OAuth Client with the Auth Keys (\`auth_keys\`) scope with write access from https://login.tailscale.com/admin/settings/oauth\033[0m" 33 | 34 | echo -e "\033[1;36mEnter your Secret Key from the step above:\033[0m" 35 | read TAILSCALE_SECRET_ID 36 | while [ -z "$TAILSCALE_SECRET_ID" ]; do 37 | echo "Tailscale secret key cannot be empty. Please enter your Tailscale secret key:" 38 | read TAILSCALE_SECRET_ID 39 | done 40 | 41 | update_env_var "base/secrets/tailscale-secrets.env" "TAILSCALE_SECRET_ID" "$TAILSCALE_SECRET_ID" 42 | 43 | echo -e "\033[1;36mEnter the Client ID from the Step Above:\033[0m" 44 | read TAILSCALE_CLIENT_ID 45 | while [ -z "$TAILSCALE_CLIENT_ID" ]; do 46 | echo "Tailscale client ID cannot be empty. Please enter your Tailscale client ID:" 47 | read TAILSCALE_CLIENT_ID 48 | done 49 | 50 | update_env_var "base/properties/api-config.env" "TAILSCALE_CLIENT_ID" "$TAILSCALE_CLIENT_ID" 51 | 52 | echo -e "\033[1;36mOn the tailscale dashboard you should see your node come online, once it does enter the IP Address of the node:\033[0m" 53 | read TAILSCALE_NODE_IP 54 | while [ -z "$TAILSCALE_NODE_IP" ]; do 55 | echo "Tailscale node IP cannot be empty. Please enter your Tailscale node IP:" 56 | read TAILSCALE_NODE_IP 57 | done 58 | 59 | update_env_var "base/properties/api-config.env" "TAILSCALE_NODE_IP" "$TAILSCALE_NODE_IP" 60 | 61 | source update.sh "$@" -------------------------------------------------------------------------------- /install-nvidia.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## Install nvidia container toolkit 4 | curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \ 5 | && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \ 6 | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \ 7 | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list 8 | 9 | apt-get update 10 | apt-get install -y nvidia-container-toolkit 11 | 12 | nvidia-ctk runtime configure --runtime=containerd 13 | 14 | systemctl restart containerd 15 | 16 | systemctl restart k3s -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$EUID" -ne 0 ]; then 4 | echo "Please run as root or with sudo" 5 | exit 1 6 | fi 7 | 8 | source setup-env.sh "$@" 9 | 10 | echo "Setup FileSystem" 11 | mkdir -p /opt/5stack/dev 12 | mkdir -p /opt/5stack/demos 13 | mkdir -p /opt/5stack/steamcmd 14 | mkdir -p /opt/5stack/serverfiles 15 | mkdir -p /opt/5stack/timescaledb 16 | mkdir -p /opt/5stack/typesense 17 | mkdir -p /opt/5stack/minio 18 | mkdir -p /opt/5stack/custom-plugins 19 | 20 | echo "Environment files setup complete" 21 | 22 | curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash 23 | 24 | curl -sfL https://get.k3s.io | sh -s - --disable=traefik 25 | 26 | output_redirect kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.1/deploy/static/provider/baremetal/deploy.yaml 27 | 28 | kubectl label node $(kubectl get nodes -o jsonpath='{.items[0].metadata.name}') 5stack-api=true 5stack-hasura=true 5stack-minio=true 5stack-timescaledb=true 5stack-redis=true 5stack-typesense=true 5stack-web=true 29 | 30 | source update.sh "$@" 31 | 32 | echo "Installed 5Stack" 33 | -------------------------------------------------------------------------------- /overlays/cert-manager/certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: Certificate 3 | metadata: 4 | namespace: 5stack 5 | name: 5stack-ssl 6 | spec: 7 | secretName: 5stack-ssl 8 | renewBefore: 240h 9 | duration: 2160h 10 | dnsNames: 11 | - ${WEB_DOMAIN} 12 | - ${API_DOMAIN} 13 | - ${DEMOS_DOMAIN} 14 | - ${TYPESENSE_HOST} 15 | - ${S3_CONSOLE_HOST} 16 | - ${WS_DOMAIN} 17 | issuerRef: 18 | name: 5stack-issuer 19 | kind: Issuer -------------------------------------------------------------------------------- /overlays/cert-manager/ingress-patch.yaml: -------------------------------------------------------------------------------- 1 | - op: add 2 | path: /metadata/annotations/cert-manager.io~1issuer 3 | value: 5stack-issuer 4 | 5 | - op: add 6 | path: /spec/tls 7 | value: 8 | - hosts: 9 | - HOST 10 | secretName: 5stack-ssl -------------------------------------------------------------------------------- /overlays/cert-manager/issuer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: Issuer 3 | metadata: 4 | namespace: 5stack 5 | name: 5stack-issuer 6 | spec: 7 | acme: 8 | server: https://acme-v02.api.letsencrypt.org/directory 9 | email: user@example.com 10 | privateKeySecretRef: 11 | name: 5stack-issuer 12 | solvers: 13 | - http01: 14 | ingress: 15 | ingressClassName: nginx 16 | -------------------------------------------------------------------------------- /overlays/cert-manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - ../../base 5 | - cert-manager.yaml 6 | - issuer.yaml 7 | - certificate.yaml 8 | 9 | replacements: 10 | - source: 11 | kind: ConfigMap 12 | name: api-config 13 | fieldPath: data.WEB_DOMAIN 14 | targets: 15 | - select: 16 | kind: Certificate 17 | name: 5stack-ssl 18 | fieldPaths: 19 | - spec.dnsNames.0 20 | - source: 21 | kind: ConfigMap 22 | name: api-config 23 | fieldPath: data.API_DOMAIN 24 | targets: 25 | - select: 26 | kind: Certificate 27 | name: 5stack-ssl 28 | fieldPaths: 29 | - spec.dnsNames.1 30 | - source: 31 | kind: ConfigMap 32 | name: api-config 33 | fieldPath: data.DEMOS_DOMAIN 34 | targets: 35 | - select: 36 | kind: Certificate 37 | name: 5stack-ssl 38 | fieldPaths: 39 | - spec.dnsNames.2 40 | - source: 41 | kind: ConfigMap 42 | name: typesense-config 43 | fieldPath: data.TYPESENSE_HOST 44 | targets: 45 | - select: 46 | kind: Certificate 47 | name: 5stack-ssl 48 | fieldPaths: 49 | - spec.dnsNames.3 50 | - source: 51 | kind: ConfigMap 52 | name: s3-config 53 | fieldPath: data.S3_CONSOLE_HOST 54 | targets: 55 | - select: 56 | kind: Certificate 57 | name: 5stack-ssl 58 | fieldPaths: 59 | - spec.dnsNames.4 60 | - source: 61 | kind: ConfigMap 62 | name: api-config 63 | fieldPath: data.WS_DOMAIN 64 | targets: 65 | - select: 66 | kind: Certificate 67 | name: 5stack-ssl 68 | fieldPaths: 69 | - spec.dnsNames.5 70 | 71 | - source: 72 | kind: ConfigMap 73 | name: api-config 74 | fieldPath: data.MAIL_FROM 75 | targets: 76 | - select: 77 | kind: Issuer 78 | name: 5stack-issuer 79 | fieldPaths: 80 | - spec.acme.email 81 | 82 | 83 | - source: 84 | kind: ConfigMap 85 | name: api-config 86 | fieldPath: data.API_DOMAIN 87 | targets: 88 | - select: 89 | kind: Ingress 90 | name: api 91 | fieldPaths: 92 | - spec.tls.0.hosts.0 93 | - source: 94 | kind: ConfigMap 95 | name: api-config 96 | fieldPath: data.DEMOS_DOMAIN 97 | targets: 98 | - select: 99 | kind: Ingress 100 | name: demos 101 | fieldPaths: 102 | - spec.tls.0.hosts.0 103 | - source: 104 | kind: ConfigMap 105 | name: typesense-config 106 | fieldPath: data.TYPESENSE_HOST 107 | targets: 108 | - select: 109 | kind: Ingress 110 | name: typesense 111 | fieldPaths: 112 | - spec.tls.0.hosts.0 113 | - source: 114 | kind: ConfigMap 115 | name: s3-config 116 | fieldPath: data.S3_CONSOLE_HOST 117 | targets: 118 | - select: 119 | kind: Ingress 120 | name: s3-console 121 | fieldPaths: 122 | - spec.tls.0.hosts.0 123 | - source: 124 | kind: ConfigMap 125 | name: api-config 126 | fieldPath: data.WEB_DOMAIN 127 | targets: 128 | - select: 129 | kind: Ingress 130 | name: web 131 | fieldPaths: 132 | - spec.tls.0.hosts.0 133 | - source: 134 | kind: ConfigMap 135 | name: api-config 136 | fieldPath: data.WS_DOMAIN 137 | targets: 138 | - select: 139 | kind: Ingress 140 | name: ws 141 | fieldPaths: 142 | - spec.tls.0.hosts.0 143 | 144 | patches: 145 | - target: 146 | group: networking.k8s.io 147 | version: v1 148 | kind: Ingress 149 | name: api 150 | path: ingress-patch.yaml 151 | - target: 152 | group: networking.k8s.io 153 | version: v1 154 | kind: Ingress 155 | name: demos 156 | path: ingress-patch.yaml 157 | - target: 158 | group: networking.k8s.io 159 | version: v1 160 | kind: Ingress 161 | name: s3-console 162 | path: ingress-patch.yaml 163 | - target: 164 | group: networking.k8s.io 165 | version: v1 166 | kind: Ingress 167 | name: typesense 168 | path: ingress-patch.yaml 169 | - target: 170 | group: networking.k8s.io 171 | version: v1 172 | kind: Ingress 173 | name: web 174 | path: ingress-patch.yaml 175 | - target: 176 | group: networking.k8s.io 177 | version: v1 178 | kind: Ingress 179 | name: ws 180 | path: ingress-patch.yaml -------------------------------------------------------------------------------- /overlays/dev/dev-cs-server/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: dev-cs-server 5 | namespace: 5stack 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: dev-cs-server 11 | template: 12 | metadata: 13 | labels: 14 | app: dev-cs-server 15 | spec: 16 | hostNetwork: true 17 | dnsPolicy: ClusterFirstWithHostNet 18 | affinity: 19 | nodeAffinity: 20 | requiredDuringSchedulingIgnoredDuringExecution: 21 | nodeSelectorTerms: 22 | - matchExpressions: 23 | - key: 5stack-dev-server 24 | operator: In 25 | values: 26 | - "true" 27 | containers: 28 | - image: ghcr.io/5stackgg/game-server:latest 29 | name: dev-cs-server 30 | ports: 31 | - containerPort: 27015 32 | protocol: TCP 33 | - containerPort: 27015 34 | protocol: UDP 35 | - containerPort: 27020 36 | protocol: TCP 37 | - containerPort: 27020 38 | protocol: UDP 39 | envFrom: 40 | - secretRef: 41 | name: dev-server-secrets 42 | env: 43 | - name: DEV_SERVER 44 | value: 'true' 45 | - name: SERVER_PORT 46 | value: '27015' 47 | - name: TV_PORT 48 | value: '27020' 49 | - name: EXTRA_GAME_PARAMS 50 | value: -maxplayers 13 +map de_overpass 51 | - name: ALLOW_BOTS 52 | value: "true" 53 | - name: STEAM_RELAY 54 | value: "true" 55 | volumeMounts: 56 | - name: steamcmd 57 | mountPath: /serverdata/steamcmd 58 | - name: serverfiles 59 | mountPath: /serverdata/serverfiles 60 | - name: demos 61 | mountPath: /opt/demos 62 | - name: custom-plugins 63 | mountPath: /opt/custom-plugins 64 | - name: dev 65 | mountPath: /opt/dev 66 | 67 | volumes: 68 | - name: steamcmd 69 | hostPath: 70 | path: /opt/5stack/steamcmd 71 | type: Directory 72 | - name: serverfiles 73 | hostPath: 74 | path: /opt/5stack/serverfiles 75 | type: Directory 76 | - name: demos 77 | hostPath: 78 | path: /opt/5stack/demos 79 | type: Directory 80 | - name: custom-plugins 81 | hostPath: 82 | path: /opt/5stack/custom-plugins 83 | type: Directory 84 | - name: dev 85 | hostPath: 86 | path: /opt/5stack/dev 87 | type: Directory 88 | -------------------------------------------------------------------------------- /overlays/dev/dev-cs-server/dev-server-secrets.env.example: -------------------------------------------------------------------------------- 1 | RCON_PASSWORD= 2 | SERVER_PASSWORD= 3 | 4 | # in the 5stack panel create a new dedicated server and get the values from there 5 | SERVER_ID= 6 | SERVER_API_PASSWORD= -------------------------------------------------------------------------------- /overlays/dev/dev-cs-server/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: 5stack 5 | 6 | resources: 7 | - deployment.yaml 8 | 9 | secretGenerator: 10 | - name: dev-server-secrets 11 | envs: 12 | - dev-server-secrets.env -------------------------------------------------------------------------------- /overlays/dev/dev-game-stream/dev-game-stream-secrets.env.example: -------------------------------------------------------------------------------- 1 | SERVER_PASSWORD= -------------------------------------------------------------------------------- /overlays/dev/dev-game-stream/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | namespace: 5stack 5 | 6 | resources: 7 | - stateful-set.yaml 8 | - service.yaml 9 | 10 | secretGenerator: 11 | - name: dev-game-stream-secrets 12 | envs: 13 | - dev-game-stream-secrets.env -------------------------------------------------------------------------------- /overlays/dev/dev-game-stream/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: dev-game-stream 5 | namespace: 5stack 6 | spec: 7 | ports: 8 | - name: vnc 9 | port: 8083 10 | protocol: TCP 11 | targetPort: 8083 12 | - name: hud 13 | port: 31982 14 | protocol: TCP 15 | targetPort: 31982 16 | 17 | selector: 18 | app: dev-game-stream 19 | type: NodePort 20 | -------------------------------------------------------------------------------- /overlays/dev/dev-game-stream/stateful-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: StatefulSet 3 | metadata: 4 | name: steam-headless 5 | spec: 6 | serviceName: "steam-headless" 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: steam-headless 11 | template: 12 | metadata: 13 | labels: 14 | app: steam-headless 15 | spec: 16 | affinity: 17 | nodeAffinity: 18 | requiredDuringSchedulingIgnoredDuringExecution: 19 | nodeSelectorTerms: 20 | - matchExpressions: 21 | - key: 5stack-dev-server 22 | operator: In 23 | values: 24 | - "true" 25 | runtimeClassName: nvidia 26 | securityContext: 27 | fsGroup: 1000 28 | containers: 29 | - name: steam-headless 30 | securityContext: 31 | privileged: true 32 | image: josh5/steam-headless:latest 33 | resources: 34 | requests: 35 | memory: "4" 36 | cpu: "4" 37 | limits: 38 | nvidia.com/gpu: 1 39 | ports: 40 | - containerPort: 8083 41 | protocol: TCP 42 | - containerPort: 31982 43 | protocol: TCP 44 | volumeMounts: 45 | - name: home-dir 46 | mountPath: /home/default/ 47 | - name: games-dir 48 | mountPath: /mnt/games/ 49 | - name: input-devices 50 | mountPath: /dev/input/ 51 | - name: dshm 52 | mountPath: /dev/shm 53 | env: 54 | - name: NAME 55 | value: 'SteamHeadless' 56 | - name: TZ 57 | value: 'America/New_York' 58 | - name: USER_LOCALES 59 | value: 'en_US.UTF-8 UTF-8' 60 | - name: DISPLAY 61 | value: ':55' 62 | - name: SHM_SIZE 63 | value: '2G' 64 | - name: DOCKER_RUNTIME 65 | value: 'nvidia' 66 | - name: PUID 67 | value: '1000' 68 | - name: PGID 69 | value: '1000' 70 | - name: UMASK 71 | value: '000' 72 | - name: USER_PASSWORD 73 | value: 'password' 74 | - name: MODE 75 | value: 'primary' 76 | - name: WEB_UI_MODE 77 | value: 'vnc' 78 | - name: ENABLE_VNC_AUDIO 79 | value: 'false' 80 | - name: PORT_NOVNC_WEB 81 | value: '8083' 82 | - name: NEKO_NAT1TO1 83 | value: '' 84 | - name: ENABLE_SUNSHINE 85 | value: 'false' 86 | - name: ENABLE_EVDEV_INPUTS 87 | value: 'true' 88 | - name: NVIDIA_DRIVER_CAPABILITIES 89 | value: 'all' 90 | - name: NVIDIA_VISIBLE_DEVICES 91 | value: 'all' 92 | volumes: 93 | - name: home-dir 94 | hostPath: 95 | path: /opt/5stack/steam/home 96 | type: Directory 97 | - name: games-dir 98 | hostPath: 99 | path: /opt/5stack/steam/games 100 | type: Directory 101 | - name: input-devices 102 | hostPath: 103 | path: /dev/input/ 104 | - name: dshm 105 | emptyDir: 106 | medium: Memory -------------------------------------------------------------------------------- /overlays/dev/game-server-node-connector/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: dev-game-server-node-connector 6 | name: dev-game-server-node-connector 7 | namespace: 5stack 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: dev-game-server-node-connector 12 | template: 13 | metadata: 14 | labels: 15 | app: dev-game-server-node-connector 16 | spec: 17 | hostNetwork: true 18 | dnsPolicy: ClusterFirstWithHostNet 19 | affinity: 20 | nodeAffinity: 21 | requiredDuringSchedulingIgnoredDuringExecution: 22 | nodeSelectorTerms: 23 | - matchExpressions: 24 | - key: 5stack-dev-server 25 | operator: In 26 | values: 27 | - "true" 28 | serviceAccountName: game-server-node-connector 29 | containers: 30 | - image: ghcr.io/5stackgg/game-server-node:latest 31 | name: dev-game-server-node-connector 32 | env: 33 | - name: NODE_NAME 34 | valueFrom: 35 | fieldRef: 36 | fieldPath: spec.nodeName 37 | envFrom: 38 | - secretRef: 39 | name: hasura-secrets 40 | - secretRef: 41 | name: redis-secrets 42 | volumeMounts: 43 | - name: server-files 44 | mountPath: /serverfiles 45 | - name: demos 46 | mountPath: /demos 47 | volumes: 48 | - name: server-files 49 | hostPath: 50 | path: /opt/5stack/serverfiles 51 | type: Directory 52 | - name: demos 53 | hostPath: 54 | path: /opt/5stack/demos 55 | type: Directory 56 | 57 | -------------------------------------------------------------------------------- /overlays/dev/game-server-node-connector/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - deployment.yaml -------------------------------------------------------------------------------- /overlays/dev/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - ../../base 6 | - dev-cs-server 7 | - dev-game-stream 8 | - game-server-node-connector -------------------------------------------------------------------------------- /overlays/nvidia/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - nvidia-plugin.yaml -------------------------------------------------------------------------------- /overlays/nvidia/nvidia-plugin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: nvidia-device-plugin-daemonset 5 | namespace: kube-system 6 | spec: 7 | selector: 8 | matchLabels: 9 | name: nvidia-device-plugin-ds 10 | template: 11 | metadata: 12 | labels: 13 | name: nvidia-device-plugin-ds 14 | spec: 15 | affinity: 16 | nodeAffinity: 17 | requiredDuringSchedulingIgnoredDuringExecution: 18 | nodeSelectorTerms: 19 | - matchExpressions: 20 | - key: 5stack-dev-server 21 | operator: In 22 | values: 23 | - "true" 24 | tolerations: 25 | - key: nvidia.com/gpu 26 | operator: Exists 27 | effect: NoSchedule 28 | priorityClassName: "system-node-critical" 29 | runtimeClassName: nvidia 30 | containers: 31 | - image: nvcr.io/nvidia/k8s-device-plugin:v0.17.0 32 | name: nvidia-device-plugin-ctr 33 | env: 34 | - name: FAIL_ON_INIT_ERROR 35 | value: "false" 36 | securityContext: 37 | allowPrivilegeEscalation: false 38 | capabilities: 39 | drop: ["ALL"] 40 | volumeMounts: 41 | - name: device-plugin 42 | mountPath: /var/lib/kubelet/device-plugins 43 | volumes: 44 | - name: device-plugin 45 | hostPath: 46 | path: /var/lib/kubelet/device-plugins -------------------------------------------------------------------------------- /overlays/weblate/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: weblate 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: weblate 10 | template: 11 | metadata: 12 | labels: 13 | app: weblate 14 | spec: 15 | containers: 16 | - name: weblate 17 | image: weblate/weblate 18 | ports: 19 | - containerPort: 8080 20 | volumeMounts: 21 | - name: weblate-data 22 | mountPath: /app/data 23 | - name: weblate-cache 24 | mountPath: /app/cache 25 | envFrom: 26 | - secretRef: 27 | name: timescaledb-secrets 28 | - configMapRef: 29 | name: weblate-config 30 | - secretRef: 31 | name: redis-secrets 32 | - name: libretranslate 33 | image: libretranslate/libretranslate:latest 34 | ports: 35 | - containerPort: 5000 36 | 37 | volumes: 38 | - name: weblate-data 39 | emptyDir: {} 40 | - name: weblate-cache 41 | emptyDir: {} -------------------------------------------------------------------------------- /overlays/weblate/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: weblate 5 | namespace: 5stack 6 | annotations: 7 | cert-manager.io/issuer: "5stack-issuer" 8 | spec: 9 | ingressClassName: nginx 10 | rules: 11 | - host: $(WEBLATE_HOST) 12 | http: 13 | paths: 14 | - backend: 15 | service: 16 | name: weblate 17 | port: 18 | number: 8080 19 | path: / 20 | pathType: Prefix 21 | tls: 22 | - hosts: 23 | - $(WEBLATE_HOST) 24 | secretName: weblate-ssl -------------------------------------------------------------------------------- /overlays/weblate/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: 5stack 4 | 5 | resources: 6 | - ../../base 7 | - deployment.yaml 8 | - ingress.yaml 9 | - service.yaml 10 | 11 | configMapGenerator: 12 | - name: weblate-config 13 | namespace: 5stack 14 | envs: 15 | - properties/weblate-config.env 16 | 17 | replacements: 18 | - source: 19 | kind: ConfigMap 20 | name: weblate-config 21 | fieldPath: data.WEBLATE_SITE_DOMAIN 22 | targets: 23 | - select: 24 | kind: Ingress 25 | name: weblate 26 | fieldPaths: 27 | - spec.tls.0.hosts.0 28 | - spec.rules.0.host -------------------------------------------------------------------------------- /overlays/weblate/properties/weblate-config.env copy.example: -------------------------------------------------------------------------------- 1 | # See Weblate documentation for detailed description: 2 | # https://docs.weblate.org/en/latest/admin/install/docker.html#generic-settings 3 | 4 | WEBLATE_SITE_DOMAIN=example.com 5 | 6 | # Weblate setup 7 | WEBLATE_DEBUG=0 8 | WEBLATE_LOGLEVEL=INFO 9 | WEBLATE_SITE_TITLE=Weblate 10 | WEBLATE_ADMIN_NAME=Weblate Admin 11 | WEBLATE_ADMIN_EMAIL=weblate@example.com 12 | WEBLATE_ADMIN_PASSWORD= 13 | WEBLATE_SERVER_EMAIL=weblate@example.com 14 | WEBLATE_DEFAULT_FROM_EMAIL=weblate@example.com 15 | WEBLATE_ALLOWED_HOSTS=* 16 | WEBLATE_REGISTRATION_OPEN=1 17 | 18 | # Extra 19 | #WEBLATE_TIME_ZONE= 20 | #WEBLATE_MT_GOOGLE_KEY= 21 | #WEBLATE_MT_GOOGLE_CREDENTIALS= 22 | #WEBLATE_MT_GOOGLE_PROJECT= 23 | #WEBLATE_MT_GOOGLE_LOCATION= 24 | #WEBLATE_SOCIAL_AUTH_GITHUB_KEY= 25 | #WEBLATE_SOCIAL_AUTH_GITHUB_SECRET= 26 | #WEBLATE_SOCIAL_AUTH_BITBUCKET_KEY= 27 | #WEBLATE_SOCIAL_AUTH_BITBUCKET_SECRET= 28 | #WEBLATE_SOCIAL_AUTH_FACEBOOK_KEY= 29 | #WEBLATE_SOCIAL_AUTH_FACEBOOK_SECRET= 30 | #WEBLATE_SOCIAL_AUTH_GOOGLE_OAUTH2_KEY= 31 | #WEBLATE_SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET= 32 | 33 | #WEBLATE_OFFLOAD_INDEXING=1 34 | #WEBLATE_GOOGLE_ANALYTICS_ID= 35 | #WEBLATE_ENABLE_HTTPS=1 36 | #WEBLATE_IP_PROXY_HEADER=HTTP_X_FORWARDED_FOR 37 | #WEBLATE_REQUIRE_LOGIN=1 38 | 39 | # LDAP Auth 40 | #WEBLATE_AUTH_LDAP_SERVER_URI=ldap://ldap.example.org 41 | #WEBLATE_AUTH_LDAP_USER_DN_TEMPLATE=uid=%(user)s,ou=People,dc=example,dc=net 42 | #WEBLATE_AUTH_LDAP_USER_ATTR_MAP=first_name:name,email:mail 43 | 44 | # PostgreSQL setup 45 | POSTGRES_USER=hasura 46 | POSTGRES_DB=weblate 47 | POSTGRES_HOST=timescaledb 48 | POSTGRES_PORT= 49 | 50 | REDIS_HOST=redis 51 | REDIS_PORT=6379 52 | 53 | # Mail server, the server has to listen on port 587 and understand TLS 54 | WEBLATE_EMAIL_HOST=127.0.0.1 55 | # Do NOT use quotes here 56 | WEBLATE_EMAIL_HOST_USER= 57 | # Do NOT use quotes here 58 | WEBLATE_EMAIL_HOST_PASSWORD= 59 | 60 | # GitLab lab setup 61 | # WEBLATE_GITLAB_USERNAME= 62 | # WEBLATE_GITLAB_HOST= 63 | # WEBLATE_GITLAB_TOKEN= 64 | 65 | CLIENT_MAX_BODY_SIZE=1000M -------------------------------------------------------------------------------- /overlays/weblate/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: weblate 5 | spec: 6 | selector: 7 | app: weblate 8 | ports: 9 | - protocol: TCP 10 | port: 8080 11 | targetPort: 8080 12 | - protocol: TCP 13 | port: 5000 14 | targetPort: 5000 15 | -------------------------------------------------------------------------------- /setup-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -n "$FIVE_STACK_ENV_SETUP" ]; then 4 | return; 5 | fi 6 | 7 | DEBUG=false 8 | FIVE_STACK_ENV_SETUP=true 9 | REVERSE_PROXY="" 10 | 11 | # Load environment variables from .5stack-env.config if it exists 12 | if [ -f .5stack-env.config ]; then 13 | source .5stack-env.config 14 | fi 15 | 16 | if [ -z "$KUBECONFIG" ]; then 17 | KUBECONFIG="/etc/rancher/k3s/k3s.yaml" 18 | fi 19 | 20 | if ! [ -f ./kustomize ] || ! [ -x ./kustomize ] 21 | then 22 | echo "kustomize not found. Installing..." 23 | curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash 24 | fi 25 | 26 | 27 | while [[ $# -gt 0 ]]; do 28 | case $1 in 29 | --kubeconfig) 30 | KUBECONFIG="$2" 31 | shift 2 32 | ;; 33 | --debug) 34 | DEBUG=true 35 | shift 36 | ;; 37 | --reverse-proxy=*) 38 | REVERSE_PROXY="${1#*=}" 39 | if [ "$REVERSE_PROXY" = "0" ] || [ "$REVERSE_PROXY" = "n" ]; then 40 | REVERSE_PROXY=false 41 | else 42 | REVERSE_PROXY=true 43 | fi 44 | shift 45 | ;; 46 | *) 47 | echo "Unknown option: $1" 48 | exit 1 49 | ;; 50 | esac 51 | done 52 | 53 | if [ "$DEBUG" = true ]; then 54 | echo "Debug mode enabled (KUBECONFIG: $KUBECONFIG, REVERSE_PROXY: $REVERSE_PROXY)" 55 | fi 56 | 57 | ask_reverse_proxy() { 58 | while true; do 59 | read -p "Are you using a reverse proxy? (http://docs.5stack.gg/install/reverse-proxy) (y/n): " use_reverse_proxy 60 | if [ "$use_reverse_proxy" = "y" ] || [ "$use_reverse_proxy" = "n" ]; then 61 | break 62 | fi 63 | echo "Please enter 'y' or 'n'" 64 | done 65 | 66 | if [ "$use_reverse_proxy" = "y" ]; then 67 | REVERSE_PROXY=true 68 | else 69 | REVERSE_PROXY=false 70 | fi 71 | } 72 | 73 | update_env_var() { 74 | local file=$1 75 | local key=$2 76 | local value=$3 77 | 78 | if [[ "$OSTYPE" == "darwin"* ]]; then 79 | sed -i '' "s|^$key=.*|$key=$value|" "$file" 80 | else 81 | sed -i "s|^$key=.*|$key=$value|" "$file" 82 | fi 83 | } 84 | 85 | output_redirect() { 86 | if [ "$DEBUG" = true ]; then 87 | "$@" 88 | else 89 | "$@" >/dev/null 90 | fi 91 | } 92 | 93 | if [ -z "$REVERSE_PROXY" ]; then 94 | ask_reverse_proxy 95 | fi 96 | 97 | if [ ! -f .5stack-env.config ]; then 98 | echo "Saving environment variables to .5stack-env.config"; 99 | 100 | # Save environment variables to .5stack-env.config 101 | cat > .5stack-env.config << EOF 102 | REVERSE_PROXY=$REVERSE_PROXY 103 | KUBECONFIG=$KUBECONFIG 104 | EOF 105 | fi 106 | 107 | for file in base/secrets/*.env.example; do 108 | env_file="${file%.example}" 109 | if [ ! -f "$env_file" ]; then 110 | cp "$file" "$env_file" 111 | fi 112 | done 113 | 114 | for file in base/properties/*.env.example; do 115 | env_file="${file%.example}" 116 | if [ ! -f "$env_file" ]; then 117 | cp "$file" "$env_file" 118 | fi 119 | done 120 | 121 | # Replace $(RAND32) with a random base64 encoded string in all non-example env files 122 | for env_file in base/secrets/*.env; do 123 | if [[ -f "$env_file" && ! "$env_file" == *.example ]]; then 124 | 125 | # Generate a random base64 encoded string 126 | random_string=$(openssl rand -base64 32 | tr '/' '_' | tr '=' '_') 127 | 128 | if [[ "$OSTYPE" == "darwin"* ]]; then 129 | sed -i '' "s/\$(RAND32)/$random_string/g" "$env_file" 130 | else 131 | sed -i "s/\$(RAND32)/$random_string/g" "$env_file" 132 | fi 133 | fi 134 | done 135 | 136 | POSTGRES_PASSWORD=$(grep "^POSTGRES_PASSWORD=" base/secrets/timescaledb-secrets.env | cut -d '=' -f2-) 137 | POSTGRES_CONNECTION_STRING="postgres://hasura:$POSTGRES_PASSWORD@timescaledb:5432/hasura" 138 | 139 | if [ -n "$POSTGRES_CONNECTION_STRING" ]; then 140 | if grep -q "^POSTGRES_CONNECTION_STRING=" base/secrets/timescaledb-secrets.env; then 141 | update_env_var "base/secrets/timescaledb-secrets.env" "POSTGRES_CONNECTION_STRING" "$POSTGRES_CONNECTION_STRING" 142 | else 143 | echo "" >> base/secrets/timescaledb-secrets.env 144 | echo "POSTGRES_CONNECTION_STRING=$POSTGRES_CONNECTION_STRING" >> base/secrets/timescaledb-secrets.env 145 | fi 146 | fi 147 | if [ -f "/var/lib/rancher/k3s/server/node-token" ]; then 148 | K3S_TOKEN=$(cat /var/lib/rancher/k3s/server/node-token) 149 | fi 150 | 151 | if [ -n "$K3S_TOKEN" ]; then 152 | if grep -q "^K3S_TOKEN=" base/secrets/api-secrets.env; then 153 | echo "K3S_TOKEN already set" 154 | update_env_var "base/secrets/api-secrets.env" "K3S_TOKEN" "$K3S_TOKEN" 155 | else 156 | echo "K3S_TOKEN not set, setting it" 157 | echo "K3S_TOKEN=$K3S_TOKEN" >> base/secrets/api-secrets.env 158 | fi 159 | fi 160 | 161 | # Using -h to suppress filename headers in grep output for Linux compatibility 162 | WEB_DOMAIN=$(grep -h "^WEB_DOMAIN=" base/properties/api-config.env | cut -d '=' -f2-) 163 | WS_DOMAIN=$(grep -h "^WS_DOMAIN=" base/properties/api-config.env | cut -d '=' -f2-) 164 | API_DOMAIN=$(grep -h "^API_DOMAIN=" base/properties/api-config.env | cut -d '=' -f2-) 165 | DEMOS_DOMAIN=$(grep -h "^DEMOS_DOMAIN=" base/properties/api-config.env | cut -d '=' -f2-) 166 | MAIL_FROM=$(grep -h "^MAIL_FROM=" base/properties/api-config.env | cut -d '=' -f2-) 167 | S3_CONSOLE_HOST=$(grep -h "^S3_CONSOLE_HOST=" base/properties/s3-config.env | cut -d '=' -f2-) 168 | TYPESENSE_HOST=$(grep -h "^TYPESENSE_HOST=" base/properties/typesense-config.env | cut -d '=' -f2-) 169 | 170 | if [ -z "$WEB_DOMAIN" ] || [ -z "$WS_DOMAIN" ] || [ -z "$API_DOMAIN" ] || [ -z "$DEMOS_DOMAIN" ] || [ -z "$MAIL_FROM" ] || [ -z "$S3_CONSOLE_HOST" ] || [ -z "$TYPESENSE_HOST" ]; then 171 | echo -e "\n\n\n\033[1;36mEnter your base domain (e.g. example.com):\033[0m" 172 | 173 | read BASE_DOMAIN 174 | while [ -z "$BASE_DOMAIN" ]; do 175 | echo "Base domain cannot be empty. Please enter your base domain (e.g. example.com):" 176 | read BASE_DOMAIN 177 | done 178 | 179 | if [ -z "$WEB_DOMAIN" ]; then 180 | WEB_DOMAIN=$BASE_DOMAIN 181 | update_env_var "base/properties/api-config.env" "WEB_DOMAIN" "$WEB_DOMAIN" 182 | fi 183 | 184 | if [ -z "$WS_DOMAIN" ]; then 185 | WS_DOMAIN="ws.$BASE_DOMAIN" 186 | update_env_var "base/properties/api-config.env" "WS_DOMAIN" "$WS_DOMAIN" 187 | fi 188 | 189 | if [ -z "$API_DOMAIN" ]; then 190 | API_DOMAIN="api.$BASE_DOMAIN" 191 | update_env_var "base/properties/api-config.env" "API_DOMAIN" "$API_DOMAIN" 192 | fi 193 | 194 | if [ -z "$DEMOS_DOMAIN" ]; then 195 | DEMOS_DOMAIN="demos.$BASE_DOMAIN" 196 | update_env_var "base/properties/api-config.env" "DEMOS_DOMAIN" "$DEMOS_DOMAIN" 197 | fi 198 | 199 | if [ -z "$MAIL_FROM" ]; then 200 | MAIL_FROM="hello@$BASE_DOMAIN" 201 | update_env_var "base/properties/api-config.env" "MAIL_FROM" "$MAIL_FROM" 202 | fi 203 | 204 | if [ -z "$S3_CONSOLE_HOST" ]; then 205 | S3_CONSOLE_HOST="console.$BASE_DOMAIN" 206 | update_env_var "base/properties/s3-config.env" "S3_CONSOLE_HOST" "$S3_CONSOLE_HOST" 207 | fi 208 | 209 | if [ -z "$TYPESENSE_HOST" ]; then 210 | TYPESENSE_HOST="search.$BASE_DOMAIN" 211 | update_env_var "base/properties/typesense-config.env" "TYPESENSE_HOST" "$TYPESENSE_HOST" 212 | fi 213 | fi 214 | 215 | STEAM_WEB_API_KEY=$(grep -h "^STEAM_WEB_API_KEY=" base/secrets/steam-secrets.env | cut -d '=' -f2-) 216 | 217 | while [ -z "$STEAM_WEB_API_KEY" ]; do 218 | echo "Please enter your Steam Web API key (required for Steam authentication). Get one at: https://steamcommunity.com/dev/apikey" 219 | read STEAM_WEB_API_KEY 220 | done 221 | 222 | update_env_var "base/secrets/steam-secrets.env" "STEAM_WEB_API_KEY" "$STEAM_WEB_API_KEY" 223 | 224 | echo "Domains and Hosts Configuration:" 225 | echo "--------------------------------" 226 | echo "WEB_DOMAIN: $WEB_DOMAIN" 227 | echo "WS_DOMAIN: $WS_DOMAIN" 228 | echo "API_DOMAIN: $API_DOMAIN" 229 | echo "DEMOS_DOMAIN: $DEMOS_DOMAIN" 230 | echo "MAIL_FROM: $MAIL_FROM" 231 | echo "S3_CONSOLE_HOST: $S3_CONSOLE_HOST" 232 | echo "TYPESENSE_HOST: $TYPESENSE_HOST" 233 | echo "--------------------------------" 234 | 235 | 236 | -------------------------------------------------------------------------------- /update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source setup-env.sh "$@" 4 | 5 | if [ "$REVERSE_PROXY" = true ]; then 6 | ./kustomize build base | output_redirect kubectl --kubeconfig=$KUBECONFIG apply -f - 7 | kubectl --kubeconfig=$KUBECONFIG delete certificate 5stack-ssl -n 5stack 2>/dev/null 8 | else 9 | ./kustomize build overlays/cert-manager | output_redirect kubectl --kubeconfig=$KUBECONFIG apply -f - 10 | fi 11 | 12 | kubectl --kubeconfig=$KUBECONFIG delete deployment minio -n 5stack 2>/dev/null 13 | kubectl --kubeconfig=$KUBECONFIG delete deployment timescaledb -n 5stack 2>/dev/null 14 | kubectl --kubeconfig=$KUBECONFIG delete deployment typesense -n 5stack 2>/dev/null 15 | 16 | GIT_SHA=$(git rev-parse HEAD) 17 | 18 | kubectl --kubeconfig=$KUBECONFIG label node $(kubectl --kubeconfig=$KUBECONFIG get nodes --selector='node-role.kubernetes.io/control-plane' -o jsonpath='{.items[0].metadata.name}') 5stack-panel-version=$GIT_SHA --overwrite 19 | 20 | echo "5Stack : Updated" 21 | -------------------------------------------------------------------------------- /weblate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while [[ $# -gt 0 ]]; do 4 | case $1 in 5 | --kubeconfig) 6 | KUBECONFIG="$2" 7 | shift 2 8 | ;; 9 | *) 10 | echo "Unknown option: $1" 11 | exit 1 12 | ;; 13 | esac 14 | done 15 | 16 | if [ -z "$KUBECONFIG" ]; then 17 | KUBECONFIG="/etc/rancher/k3s/k3s.yaml" 18 | fi 19 | 20 | ./kustomize build overlays/weblate | kubectl --kubeconfig=$KUBECONFIG apply -f - --------------------------------------------------------------------------------