# Scale the whole chart down without uninstalling it: scaleDown: true renders # every Deployment/StatefulSet with replicas: 0 (and suspends CronJobs) and # takes precedence over any per-workload replicas value. PVCs, Secrets and # Services stay in place; scaleDown: false brings the workloads back up. scaleDown: false # Erasing this block (dev: with no sub-keys, or --set dev=null) falls back to # the defaults below instead of aborting the render — same for game, server and # the hardening/probe/resources blocks (Gitea issue #16, issue #99). dev: debug: false game: experimental: false maxPlayers: 4 # Seasonal (holiday) events. The image only knows the negative switch # DISABLESEASONALEVENTS, so the chart NEGATES this value on the way in: # enableSeasonalEvents: true renders DISABLESEASONALEVENTS="false" (events # run), false renders "true" (events off). Only true/false are accepted, in # boolean or string form; anything else aborts the render instead of being # read as one of the two. # # The default is false on purpose, and it is not a preference: it reproduces # what this chart has ALWAYS rendered. Up to 6.1.0 the key was called # game.seasonalEvents and was passed through UNCHANGED, so its default true # wrote DISABLESEASONALEVENTS=true — the events were off in every install, # while values.schema.json claimed "true means events are enabled" (#215). # Keeping the rendered result identical is what makes this rename loud # instead of silent: nobody's server changes behaviour by upgrading, and the # old key is now rejected by the schema, so anyone who set it has to decide # again. Set enableSeasonalEvents: true to actually switch the events on. enableSeasonalEvents: false # Erasing this block, or any single key in it, falls back to the defaults below # (Gitea issue #16) — with the documented exception of storage, see there. # Before 7.0.1 "server: null" aborted the render, and a single erased key was # the quieter half: a nulled gamePort rendered an empty port number into the # Service and the StatefulSet, rejected by the API server at apply time and by # nothing before it. server: # Replica count for this workload; 0 is allowed and scales it down. # The chart-global scaleDown flag takes precedence. replicas: 1 gamePort: 30777 messagingPort: 30888 # The size of the data PVC, and the ONE key of this block that is NOT # null-safe — deliberately, not by oversight. An erased storage (including # "server: null") renders a PVC without a size, which fails at apply time # instead of quietly sizing the volume from the chart. Whether that is still # the right trade is negotiated in Gitea issue #39, not here. storage: 20Gi resources: # Limits are optional: memory and ephemeral-storage limits default to # limitFactor x the request, an explicitly set limit always wins, and a # CPU limit is only set when configured explicitly under "limits". # The game server holds a large world in memory, so the default factor # stays at 2 to keep the limit at the previously published 8Gi. limitFactor: 2 requests: memory: "4Gi" cpu: "1" ephemeral-storage: "5Gi" # Extra environment variables for the server container, appended to the ones # the chart always sets (DEBUG, MAXPLAYERS, PGID, PUID, SKIPUPDATE, STEAMBETA, # DISABLESEASONALEVENTS, SERVERGAMEPORT, SERVERMESSAGINGPORT). Values are # rendered through tpl and support the value, secretKeyRef and configMapKeyRef # forms: # - name: SOME_ENV # value: "{{ .Release.Name }}" # - name: FROM_SECRET # valueFrom: # secretKeyRef: # name: "some-secret" # key: "someKey" # - name: FROM_CONFIGMAP # valueFrom: # configMapKeyRef: # name: "some-configmap" # key: "someKey" env: [] # The image's healthcheck script queries the server's HTTPS API on the game # port (SERVERGAMEPORT), so liveness/startup keep working under port overrides. livenessProbe: exec: command: - "/bin/sh" - "-c" - "/home/steam/healthcheck.sh" failureThreshold: 4 successThreshold: 1 periodSeconds: 30 timeoutSeconds: 10 readinessProbe: tcpSocket: port: api periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 # The startup probe gates liveness/readiness. The server downloads / updates # the game files via Steam on startup, which can take a long time: # budget 30s * 40 = 20 minutes. startupProbe: exec: command: - "/bin/sh" - "-c" - "/home/steam/healthcheck.sh" periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 40 securityContext: pod: runAsNonRoot: true # The wolveix/satisfactory-server image supports rootless operation only # as its built-in "steam" user (uid/gid 1000) - the entrypoint rejects any # other non-root uid/gid. The PUID/PGID env vars only apply to the root # entrypoint path and are ignored when running non-root. runAsUser: 1000 runAsGroup: 1000 # fsGroup keeps the data volume writable for the steam user. # OnRootMismatch (instead of Always) skips the recursive chown/chmod of # the multi-GB game files on every pod start; volumes provisioned by chart # versions < 4.0.0 were already chowned to 1000:1000 by the image's root # entrypoint. fsGroup: 1000 fsGroupChangePolicy: OnRootMismatch # RuntimeDefault applies the container runtime's default seccomp filter, # which blocks the syscalls a normal workload never makes. It sits in the # pod context on purpose: from here it covers every container in the pod # including init containers, while a container-level seccompProfile would # have to be repeated for each one (and silently missed on the next one # added). A container that genuinely needs a different filter can still # override it under securityContext.container, which wins over this value. seccompProfile: type: RuntimeDefault container: allowPrivilegeEscalation: false # Works because the chart mounts emptyDirs at /tmp and the steam user's # ~/.steam, ~/.config and ~/.local (steamcmd bootstraps itself into # ~/.local/share/Steam on start); all other writes go to the PVC at # /config. readOnlyRootFilesystem: true runAsNonRoot: true capabilities: drop: - ALL add: [] # --------------------------------------------------------------------------- # NetworkPolicy (Gitea #5). On by default. The reference implementation with # the full reasoning is template-chart; the convention is in README, "Chart # style guidelines", and the operator's view on this chart's wiki page. # --------------------------------------------------------------------------- networkPolicy: # enabled IS THE ONLY VALUE HERE, and that is deliberate. The single inbound # rule is open to any source and there is no egress section at all (see # below), so allowedNamespaces and probeSources would both reach no rule - # and a value that silently does nothing is worse than one that is absent, # because it reads as a control. # # WHAT THIS POLICY ALLOWS: inbound the three declared game ports only, open # to any source - they are NodePorts and the clients are players on the # internet. THERE IS NO EGRESS SECTION, and that is the decision in this # chart rather than an omission. # # TWO REASONS, AND THEY COMPOUND. First, the outbound requirement is Steam's # content delivery, which is not a pinnable port list: SteamCMD uses TCP 80, # 443 and 27015-27050, the master servers UDP 27000-27100 and 4380, Valve # changes the set, and the server re-downloads on every update. Second, and # decisively: satisfactory is the ONE chart here with no install-test at all # (ci/excluded-charts.txt - the image is multiple GB and the first start # pulls over 10GB). An egress cap here would be the only one in the sweep # with no empirical check behind it, on the chart whose correct port set is # least knowable, and a review cannot verify a Steam port range. # # So policyTypes names Ingress only, for the same reason valkey does: a rule # that has to be widened until it permits everything makes the object look # stricter than it is. A deployment that wants an egress cap should write one # at release level, where Steam's behaviour can be observed. # # The game port is allowed as BOTH TCP and UDP, because the server binds it # as both - a TCP-only rule would be accepted and would drop half the game # traffic. enabled: true