init commit

This commit is contained in:
2026-10-28 12:06:51 -06:00
parent d5a2c13b00
commit f82e56ea27
9 changed files with 352 additions and 0 deletions

39
ansible-playbook.yml Executable file
View File

@@ -0,0 +1,39 @@
---
- hosts: internal-apps
become: true
vars_files:
- ./vars.yml
tasks:
- name: Pull images
containers.podman.podman_image:
name: "{{ item }}"
loop: "{{ images }}"
tags:
- images
roles:
- role: packages
tags: [packages]
- role: directories
tags: [directories]
- role: sysctl
tags: [sysctl]
- role: network
tags: [network]
- role: wireguard
tags: [wireguard]
- role: npm
tags: [npm]
- role: gitea
tags: [gitea]
- role: jellyfin
tags: [jellyfin]
- role: wazuh
tags: [wazuh]
# - role: minecraft
# tags: [minecraft]
# - role: 7dtd
# tags: [7dtd]
# - role: zomboid
# tags: [zomboid]

2
inventory.ini Executable file
View File

@@ -0,0 +1,2 @@
[internal-apps]
7dtd ansible_user=fedora ansible_become_password="VjocLlmw6faSdM"

5
requirements.yml Executable file
View File

@@ -0,0 +1,5 @@
collections:
- name: containers.podman
- name: ansible.posix

View File

Binary file not shown.

View File

@@ -0,0 +1,33 @@
<configs>
<!--
More Animals per Area - v3.x spawn-density patch for the dedicated server.
Written for the v3.0+ spawning.xml schema (validated against the v3.0 shipped file):
- maxcount is now per 80x80m "biome chunk" (vanilla = 1 for every animal group).
Friendly groups: WildGameForest (day/Any, all biomes), WildGameForestNight
(pine/burnt), WildGameStagNight (snow/wasteland). Predators: EnemyAnimalsForest,
EnemyAnimalsBurntForestAny/Night, EnemyAnimalsDesert/Night, EnemyAnimalsSnow/Night,
EnemyAnimalsWasteland/Night. The starts-with() selectors cover ALL current and future
variants and cannot hit non-animal spawns (EnemyVulture*, ZombiesAll do not match).
- respawndelay is SIX comma-separated game-day values, one per sandbox Respawn speed:
Default,VerySlow,Slow,Normal,Fast,VeryFast (multipliers 1.75,1.35,1,0.65,0.35).
A single scalar corrupts the entry on v3.x. Base values: 0.25 for friendly game
(~6x faster than the 1.5 vanilla), 0.6 for night/predator groups.
- GATE: XML mods are only honored while the sandbox "Density" options (Day/Night Animal
+ Enemy) are set to Default. If a SandboxCode sets any density != Default, THIS PATCH
IS IGNORED and the sandbox value wins - use SandboxCode density in that case.
- EnemyAnimalsSnowNight / EnemyAnimalsWasteland* are zombie animals (undead), included
by the selector but worth knowing about.
Effect vs vanilla (pine forest): WildGameForest 1->4 alive per 80x80m chunk, respawn
1.5d->0.25d; predators 1->3, respawn 2.9d->0.6d - roughly a 3-4x wildlife increase.
-->
<!-- Friendly animals (day + night, all biomes): 4 alive per chunk, base respawn 0.25d -->
<set xpath="/spawning/biome/spawn[starts-with(@entitygroup,'WildGame')]/@maxcount">4</set>
<set xpath="/spawning/biome/spawn[starts-with(@entitygroup,'WildGame')]/@respawndelay">0.25,0.438,0.338,0.25,0.163,0.088</set>
<!-- Hostile animals (all biome variants incl. zombie-animal night groups): 3 alive per chunk, base respawn 0.6d -->
<set xpath="/spawning/biome/spawn[starts-with(@entitygroup,'EnemyAnimals')]/@maxcount">3</set>
<set xpath="/spawning/biome/spawn[starts-with(@entitygroup,'EnemyAnimals')]/@respawndelay">0.6,1.05,0.81,0.6,0.39,0.21</set>
</configs>

View File

@@ -0,0 +1,7 @@
<xml>
<Name value="MoreAnimals"/>
<DisplayName value="More Animals per Area"/>
<Version value="1.1"/>
<Description value="Raises wild-game and predator spawn density (maxcount) and respawn rate in every biome. Pure XML data patch - no DLLs, no client install, EAC-safe."/>
<Author value="boxswapper"/>
</xml>

135
roles/7dtd/tasks/main.yml Normal file
View File

@@ -0,0 +1,135 @@
---
# 7 Days to Die dedicated server (vinanrra/7dtd-server container, LinuxGSM-based, SteamCMD-installed).
# PUBLIC server: players connect to 7dtd.boxswapper.io:26900 (TCP+UDP) and enter the join password
# (sdtd_server_password). Server config is rendered from roles/7dtd/templates/sdtdserver.xml.j2 on
# every deploy - vars.yml is the source of truth for the server config.
# External prerequisites (NOT managed by this repo):
# - Cloudflare A record 7dtd.boxswapper.io -> public IP (grey-cloud, like mc.boxswapper.io)
# - Router port forwards: TCP+UDP 26900, UDP 26901-26902 + TCP 8081 (telnet) -> 10.10.0.47
# - CHANGED sdtd_server_password / sdtd_telnet_password in vars.yml before first deploy
- name: Create 7 Days to Die data directory
ansible.builtin.file:
path: /opt/apps/7dtd/data
state: directory
mode: '0755'
# The container (linuxserver-style base) runs as uid/gid 1000 (PUID/PGID) and chowns its volumes on
# start; pre-seeding with that ownership lets the init script and server write alongside our xml.
# NOTE: NO /etc/timezone or /etc/localtime bind mounts here (unlike minecraft/zomboid) - the image's
# user.sh writes BOTH paths itself to apply the TimeZone env var, so ro binds crash-loop the container.
- name: Create 7 Days to Die saves dir (container user)
ansible.builtin.file:
path: /opt/apps/7dtd/data/7DaysToDie
state: directory
owner: '1000'
group: '1000'
mode: '0755'
- name: Create 7 Days to Die serverfiles directory (container user)
ansible.builtin.file:
path: /opt/apps/7dtd/serverfiles
state: directory
owner: '1000'
group: '1000'
mode: '0755'
# Mods live in the bind-mounted serverfiles; the container loads them on its next start
# (recreate on every deploy re-reads them). zips come from roles/7dtd/files/mods (sdtd_mods
# in vars.yml), extracted one level deep (each zip carries its own mod subfolder; prefix
# folders with z- if they must sort LAST for load order).
# Deployed now: MoreAnimals — a PURE XML data patch (no DLL/Harmony) raising wild-game/predator
# spawn density (v3.x schema: maxcount per 80x80m chunk + 6-value respawndelay tuples). Works
# with EAC ON, needs NO client install (spawning is server-authoritative; the server pushes its
# XML state to clients on connect). GATE: XML values apply only while the sandbox Density options
# are Default — once sdtd_sandbox_code sets any density != Default, the sandbox value wins and
# this patch is inert; use SandboxCode Day/Night Animal Density + Respawn instead.
# unarchive only ADDS — it never removes, so a mod dropped from sdtd_mods would keep loading:
# with an empty list we wipe the whole dir and the create-task below rebuilds it empty.
# Partial mod swaps are not auto-cleaned (the role only knows zip names, not the folders zips
# unpack into) — remove those retired folders from the box by hand.
- name: Remove 7 Days to Die Mods directory when no mods are deployed
ansible.builtin.file:
path: /opt/apps/7dtd/serverfiles/Mods
state: absent
when: sdtd_mods | length == 0
- name: Create 7 Days to Die Mods directory (container user)
ansible.builtin.file:
path: /opt/apps/7dtd/serverfiles/Mods
state: directory
owner: '1000'
group: '1000'
mode: '0755'
- name: Deploy 7 Days to Die mods from vendored zips
ansible.builtin.unarchive:
src: "mods/{{ item }}"
dest: /opt/apps/7dtd/serverfiles/Mods
owner: '1000'
group: '1000'
mode: '0755'
loop: "{{ sdtd_mods }}"
- name: Template 7 Days to Die serverconfig from vars.yml
ansible.builtin.template:
src: sdtdserver.xml.j2
dest: /opt/apps/7dtd/serverfiles/sdtdserver.xml
owner: '1000'
group: '1000'
mode: '0644'
# LinuxGSM reads config-lgsm/sdtdserver/sdtdserver.cfg on EVERY invocation (start/monitor/stop).
# Bind-mounting it makes the role the owner of that config (see vars.yml sdtd_lgsm_querymode);
# a previously unmanaged podman volume left defaults in place, causing the monitor crash-loop.
- name: Create 7 Days to Die LGSM config directory (container user)
ansible.builtin.file:
path: /opt/apps/7dtd/lgsm
state: directory
owner: '1000'
group: '1000'
mode: '0755'
- name: Template 7 Days to Die LGSM config from vars.yml
ansible.builtin.template:
src: sdtdserver.cfg.j2
dest: /opt/apps/7dtd/lgsm/sdtdserver.cfg
owner: '1000'
group: '1000'
mode: '0644'
- name: Deploy 7 Days to Die Server
containers.podman.podman_container:
image: "{{ sdtd_image }}"
name: 7dtd
env: "{{ sdtd_env_base }}"
restart_policy: always
recreate: true
state: started
volumes:
- /opt/apps/7dtd/data:/home/sdtdserver/.local/share/7DaysToDie:z
- /opt/apps/7dtd/serverfiles:/home/sdtdserver/serverfiles:z
- /opt/apps/7dtd/lgsm:/home/sdtdserver/lgsm/config-lgsm/sdtdserver:z
ports:
# Required: game port (TCP+UDP), Steam query/beacon ports (UDP)
- "26900:26900/tcp"
- "26900:26900/udp"
- "26901:26901/udp"
- "26902:26902/udp"
# Optional: telnet admin console (gated by sdtd_telnet_password)
- "8081:8081/tcp"
network:
- npm_internal
ip: '{{ sdtd_ip }}'
vars:
# sdtd_env_base: START_MODE=1 starts the server; PUID/PGID match the pre-seeded dir ownership;
# MONITOR=YES restarts the game process if it crashes (LinuxGSM cron).
# IMPORTANT: restart_policy: always is only valid with START_MODE=1 (never 0/2/4 per image docs).
sdtd_env_base:
START_MODE: "{{ sdtd_start_mode }}"
VERSION: "{{ sdtd_version }}"
PUID: "{{ sdtd_puid }}"
PGID: "{{ sdtd_pgid }}"
TimeZone: "{{ sdtd_timezone }}"
BACKUP: "{{ sdtd_backup }}"
MONITOR: "{{ sdtd_monitor }}"
TEST_ALERT: "NO"

View File

@@ -0,0 +1,14 @@
##################################
####### Instance Settings ########
##################################
# Managed by Ansible (roles/7dtd/templates/sdtdserver.cfg.j2) - local edits are overwritten on re-run.
# Source of truth: sdtd_* variables in vars.yml.
## 7dtd answers NO A2S query on the game port (ServerVisibility=0 + SteamNetworking disabled),
## so the default gamedig-based monitor (querymode=2) always fails and force-restarts the healthy
## server every ~5 min. querymode=1 checks the tmux session instead.
querymode="{{ sdtd_lgsm_querymode }}"
## LGSM graceful stop reads ${telnetpassword} from this cfg (the game XML TelnetPassword is parsed
## into ${telnetpass}, a different var). Without it, stop falls back to a hard tmux kill.
telnetpassword="{{ sdtd_telnet_password }}"

View File

@@ -0,0 +1,117 @@
<?xml version="1.0"?>
<!-- Managed by Ansible (roles/7dtd/templates/sdtdserver.xml.j2) - local edits are overwritten on re-run. -->
<!-- Source of truth: sdtd_* variables in vars.yml. Applied on every deploy (template task runs each time). -->
<ServerSettings>
<!-- GENERAL SERVER SETTINGS -->
<!-- Server representation -->
<property name="ServerName" value="{{ sdtd_servername }}" />
<property name="ServerDescription" value="{{ sdtd_server_description }}" />
<property name="ServerWebsiteURL" value="{{ sdtd_website_url }}" />
<property name="ServerPassword" value="{{ sdtd_server_password }}" />
<property name="ServerLoginConfirmationText" value="{{ sdtd_server_confirmation }}" />
<property name="Region" value="{{ sdtd_region }}" />
<property name="Language" value="{{ sdtd_language }}" />
<!-- Networking -->
<property name="ServerPort" value="{{ sdtd_port }}" />
<!-- Visibility: 2 = public, 1 = shown to friends only, 0 = not listed (private, direct-connect only) -->
<property name="ServerVisibility" value="{{ sdtd_visibility }}" />
<!-- Public servers behind correct port forwards should disable SteamNetworking (no NAT relay needed) -->
<property name="ServerDisabledNetworkProtocols" value="{{ sdtd_disabled_network_protocols }}" />
<property name="ServerMaxWorldTransferSpeedKiBs" value="512" />
<!-- Slots -->
<property name="ServerMaxPlayerCount" value="{{ sdtd_max_players }}" />
<property name="ServerReservedSlots" value="0" />
<property name="ServerReservedSlotsPermission" value="100" />
<property name="ServerAdminSlots" value="0" />
<property name="ServerAdminSlotsPermission" value="0" />
<!-- Admin interfaces -->
<property name="WebDashboardEnabled" value="false" />
<property name="WebDashboardPort" value="8080" />
<property name="WebDashboardUrl" value="" />
<property name="EnableMapRendering" value="false" />
<property name="TelnetEnabled" value="{{ sdtd_telnet_enabled | lower }}" />
<property name="TelnetPort" value="8081" />
<property name="TelnetPassword" value="{{ sdtd_telnet_password }}" />
<property name="TelnetFailedLoginLimit" value="10" />
<property name="TelnetFailedLoginsBlocktime" value="10" />
<property name="TerminalWindowEnabled" value="true" />
<!-- Folder and file locations -->
<property name="AdminFileName" value="serveradmin.xml" />
<!-- Other technical settings -->
<property name="EACEnabled" value="{{ sdtd_eac_enabled | lower }}" />
<property name="HideCommandExecutionLog" value="0" />
<property name="MaxUncoveredMapChunksPerPlayer" value="131072" />
<property name="PersistentPlayerProfiles" value="false" />
<!-- GAMEPLAY -->
<!-- World. GameWorld: "RWG" for random gen (uses WorldGenSeed/WorldGenSize below) or an existing
world name ("Navezgane", "PREGEN01", ...). GameName sets the save/decoration seed name. -->
<property name="GameWorld" value="{{ sdtd_game_world }}" />
<property name="WorldGenSeed" value="{{ sdtd_worldgen_seed }}" />
<property name="WorldGenSize" value="{{ sdtd_worldgen_size }}" />
<property name="GameName" value="{{ sdtd_game_name }}" />
<property name="GameMode" value="{{ sdtd_game_mode }}" />
<!-- v3.x GAMEPLAY TUNING MOVED TO SANDBOXCODE: GameDifficulty, XPMultiplier, DayNightLength,
DayLightLength, DeathPenalty, DropOnDeath/DropOnQuit, zombie speeds (ZombieMove etc.),
BloodMoon*, LootAbundance, LootRespawnDays, AirDropFrequency, BlockDamage*, EnemySpawnMode
were REMOVED from serverconfig.xml in v3.0 and are silently IGNORED - the game reads only
the SandboxCode. Empty code = Adventurer defaults. To restore the server's intended tuning:
client -> New Game -> Sandbox Options -> mirror the sdtd_* reference vars in vars.yml ->
copy code -> set sdtd_sandbox_code in vars.yml. MaxSpawnedZombies/MaxSpawnedAnimals remain
live serverconfig properties (see below). -->
{% if sdtd_sandbox_code %}
<property name="SandboxCode" value="{{ sdtd_sandbox_code }}" />
{% endif %}
<!-- Still-live v3.x gameplay properties (not moved to sandbox) -->
<property name="PlayerSafeZoneLevel" value="5" />
<property name="PlayerSafeZoneHours" value="5" />
<property name="BuildCreate" value="false" />
<property name="BedrollDeadZoneSize" value="15" />
<property name="BedrollExpiryTime" value="45" />
<!-- Performance / spawn caps (live properties; animal density itself is patched by the
MoreAnimals modlet on Data/Config/spawning.xml - XML values apply only while the sandbox
Density options are Default) -->
<property name="MaxSpawnedZombies" value="{{ sdtd_max_spawned_zombies }}" />
<property name="MaxSpawnedAnimals" value="{{ sdtd_max_spawn_animals }}" />
<property name="ServerMaxAllowedViewDistance" value="12" />
<property name="MaxQueuedMeshLayers" value="1000" />
<!-- Multiplayer. PlayerKillingMode: 0 = no killing, 1 = kill allies only, 2 = kill strangers only, 3 = kill everyone -->
<property name="AirDropMarker" value="true" />
<property name="PartySharedKillRange" value="100" />
<property name="PlayerKillingMode" value="{{ sdtd_player_killing_mode }}" />
<!-- Land claim options -->
<property name="LandClaimCount" value="3" />
<property name="LandClaimSize" value="41" />
<property name="LandClaimDeadZone" value="30" />
<property name="LandClaimExpiryTime" value="7" />
<property name="LandClaimDecayMode" value="0" />
<property name="LandClaimOnlineDurabilityModifier" value="4" />
<property name="LandClaimOfflineDurabilityModifier" value="4" />
<property name="LandClaimOfflineDelay" value="0" />
<property name="DynamicMeshEnabled" value="true" />
<property name="DynamicMeshLandClaimOnly" value="true" />
<property name="DynamicMeshLandClaimBuffer" value="3" />
<property name="DynamicMeshMaxItemCache" value="3" />
<property name="TwitchServerPermission" value="90" />
<property name="TwitchBloodMoonAllowed" value="false" />
<property name="MaxChunkAge" value="-1" />
<property name="SaveDataLimit" value="-1" />
</ServerSettings>