--- - name: create zomboid host directory volumes become: true ansible.builtin.file: path: "{{ item }}" state: directory owner: "{{ podman_subuid.stdout }}" group: "{{ podman_user }}" mode: 0755 notify: restorecon podman loop: - "{{ zomboid_path }}/server" - "{{ zomboid_path }}/data" - "{{ zomboid_path }}/scripts" - "{{ zomboid_path }}/logs" - name: create podman bin directory become: true ansible.builtin.file: path: "{{ podman_home }}/bin" state: directory owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0755' - name: deploy zomboid world reset script become: true ansible.builtin.template: src: zomboid/world-reset.sh.j2 dest: "{{ podman_home }}/bin/zomboid-world-reset.sh" owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0755' - name: deploy zomboid world reset path unit become: true ansible.builtin.template: src: zomboid/zomboid-world-reset.path.j2 dest: "{{ podman_home }}/.config/systemd/user/zomboid-world-reset.path" owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0644' notify: reload zomboid systemd - name: deploy zomboid world reset service unit become: true ansible.builtin.template: src: zomboid/zomboid-world-reset.service.j2 dest: "{{ podman_home }}/.config/systemd/user/zomboid-world-reset.service" owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0644' notify: reload zomboid systemd - name: deploy zomboid stats script become: true ansible.builtin.template: src: zomboid/zomboid-stats.sh.j2 dest: "{{ podman_home }}/bin/zomboid-stats.sh" owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0755' - name: create zomboid stats file with correct permissions become: true ansible.builtin.file: path: "{{ podman_volumes }}/zomboid-stats.json" state: touch owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0644' modification_time: preserve access_time: preserve - name: deploy zomboid stats service unit become: true ansible.builtin.template: src: zomboid/zomboid-stats.service.j2 dest: "{{ podman_home }}/.config/systemd/user/zomboid-stats.service" owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0644' notify: reload zomboid systemd - name: deploy zomboid stats timer unit become: true ansible.builtin.template: src: zomboid/zomboid-stats.timer.j2 dest: "{{ podman_home }}/.config/systemd/user/zomboid-stats.timer" owner: "{{ podman_user }}" group: "{{ podman_user }}" mode: '0644' notify: reload zomboid systemd - name: enable zomboid stats timer become: true become_user: "{{ podman_user }}" ansible.builtin.systemd: name: zomboid-stats.timer scope: user enabled: true state: started daemon_reload: true - name: copy zomboid entrypoint script become: true ansible.builtin.template: src: zomboid/entrypoint.sh.j2 dest: "{{ zomboid_path }}/scripts/entrypoint.sh" owner: "{{ podman_subuid.stdout }}" group: "{{ podman_user }}" mode: 0755 notify: restorecon podman - name: copy zomboid steamcmd install script become: true ansible.builtin.template: src: zomboid/install.scmd.j2 dest: "{{ zomboid_path }}/scripts/install.scmd" owner: "{{ podman_subuid.stdout }}" group: "{{ podman_user }}" mode: 0644 notify: restorecon podman # Set volume permissions for steam user (UID 1000) inside container # This uses podman unshare to set ownership correctly for rootless podman - name: set zomboid volume permissions for steam user become: true become_user: "{{ podman_user }}" ansible.builtin.shell: | podman unshare chown -R 1000:1000 {{ zomboid_path }}/server podman unshare chown -R 1000:1000 {{ zomboid_path }}/data changed_when: false - name: flush handlers ansible.builtin.meta: flush_handlers - import_tasks: podman/podman-check.yml vars: container_name: zomboid container_image: "{{ image }}" - name: create zomboid container become: true become_user: "{{ podman_user }}" containers.podman.podman_container: name: zomboid image: "{{ image }}" restart_policy: on-failure:3 log_driver: journald env: SERVER_NAME: "{{ zomboid_server_names[zomboid_server_mode] }}" MIN_RAM: 8g MAX_RAM: 24g AUTO_UPDATE: "true" ADMIN_PASSWORD: "{{ zomboid_admin_password }}" SERVER_PASSWORD: "{{ zomboid_password }}" PUID: "1000" PGID: "1000" volumes: - "{{ zomboid_path }}/server:/project-zomboid" - "{{ zomboid_path }}/data:/project-zomboid-config" - "{{ zomboid_path }}/scripts/entrypoint.sh:/entrypoint.sh:ro" - "{{ zomboid_path }}/scripts/install.scmd:/home/steam/install.scmd:ro" ports: - "16261:16261/udp" - "16262:16262/udp" - "{{ zomboid_rcon_port }}:{{ zomboid_rcon_port }}/tcp" command: /bin/bash /entrypoint.sh - name: create systemd startup job for zomboid include_tasks: podman/systemd-generate.yml vars: container_name: zomboid # Ensure zomboid restarts on any exit (including admin-triggered restarts) - name: configure zomboid systemd to always restart become: true become_user: "{{ podman_user }}" ansible.builtin.lineinfile: path: "{{ podman_home }}/.config/systemd/user/zomboid.service" regexp: "^Restart=" line: "Restart=always" notify: reload zomboid systemd # Check if server INI exists (generated on first server run) - name: check if zomboid server ini exists become: true ansible.builtin.stat: path: "{{ zomboid_path }}/data/Server/{{ zomboid_server_names[zomboid_server_mode] }}.ini" register: zomboid_ini_stat tags: zomboid-conf # Backup settings (requires server to have run once to generate ini) - name: configure zomboid backup settings become: true ansible.builtin.lineinfile: path: "{{ zomboid_path }}/data/Server/{{ zomboid_server_names[zomboid_server_mode] }}.ini" regexp: "^{{ item.key }}=" line: "{{ item.key }}={{ item.value }}" loop: - { key: "SaveWorldEveryMinutes", value: "10" } - { key: "BackupsPeriod", value: "30" } - { key: "BackupsCount", value: "10" } # B42 Linux server fix: disable Lua checksum to allow mods to load - { key: "DoLuaChecksum", value: "false" } # Server password - { key: "Password", value: "{{ zomboid_password }}" } when: zomboid_ini_stat.stat.exists tags: zomboid-conf # Discord integration (uses Gregbot token, posts /all chat to Discord) - name: configure zomboid discord integration become: true ansible.builtin.lineinfile: path: "{{ zomboid_path }}/data/Server/{{ zomboid_server_names[zomboid_server_mode] }}.ini" regexp: "^{{ item.key }}=" line: "{{ item.key }}={{ item.value }}" loop: - { key: "DiscordEnable", value: "true" } - { key: "DiscordToken", value: "{{ zomboid_discord_token }}" } - { key: "DiscordChannel", value: "zomboidbot" } - { key: "DiscordChannelID", value: "1451961291194241095" } when: zomboid_ini_stat.stat.exists tags: zomboid-conf # RCON configuration for remote administration - name: configure zomboid rcon become: true ansible.builtin.lineinfile: path: "{{ zomboid_path }}/data/Server/{{ zomboid_server_names[zomboid_server_mode] }}.ini" regexp: "^{{ item.key }}=" line: "{{ item.key }}={{ item.value }}" loop: - { key: "RCONPort", value: "{{ zomboid_rcon_port }}" } - { key: "RCONPassword", value: "{{ zomboid_admin_password }}" } when: zomboid_ini_stat.stat.exists tags: zomboid-conf # Mod configuration for 'modded' server profile - name: configure zomboid mods for modded server become: true ansible.builtin.lineinfile: path: "{{ zomboid_path }}/data/Server/{{ zomboid_server_names[zomboid_server_mode] }}.ini" regexp: "^{{ item.key }}=" line: "{{ item.key }}={{ item.value }}" loop: - { key: "Mods", value: "{{ zomboid_mods.mod_ids }}" } - { key: "WorkshopItems", value: "{{ zomboid_mods.workshop_items }}" } when: - zomboid_server_mode == 'modded' - zomboid_ini_stat.stat.exists tags: zomboid-conf # Mod configuration for 'b42revamp' server profile (168 mods from Steam collection) - name: configure zomboid mods for b42revamp server become: true ansible.builtin.lineinfile: path: "{{ zomboid_path }}/data/Server/{{ zomboid_server_names[zomboid_server_mode] }}.ini" regexp: "^{{ item.key }}=" line: "{{ item.key }}={{ item.value }}" loop: - { key: "Mods", value: "{{ zomboid_mods_b42revamp.mod_ids }}" } - { key: "WorkshopItems", value: "{{ zomboid_mods_b42revamp.workshop_items }}" } when: - zomboid_server_mode == 'b42revamp' - zomboid_ini_stat.stat.exists tags: zomboid-conf # Map configuration (for modded servers with custom maps) - name: configure zomboid map become: true ansible.builtin.lineinfile: path: "{{ zomboid_path }}/data/Server/{{ zomboid_server_names[zomboid_server_mode] }}.ini" regexp: "^Map=" line: "Map={{ zomboid_maps[zomboid_server_mode] }}" when: - zomboid_server_mode != 'vanilla' - zomboid_ini_stat.stat.exists tags: zomboid-conf # Firewall logging for player IP correlation # Logs new UDP connections to Zomboid port for IP address tracking - name: add firewall rule to log zomboid connections become: true ansible.builtin.command: > firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p udp --dport 16261 -m conntrack --ctstate NEW -j LOG --log-prefix "ZOMBOID_CONN: " --log-level 4 register: firewall_result changed_when: "'already' not in firewall_result.stderr" failed_when: false notify: restart firewalld - name: add firewall rule to log zomboid connections (runtime) become: true ansible.builtin.command: > firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p udp --dport 16261 -m conntrack --ctstate NEW -j LOG --log-prefix "ZOMBOID_CONN: " --log-level 4 changed_when: false failed_when: false # World reset is now triggered via Discord bot -> systemd path unit # See zomboid-world-reset.path and zomboid-world-reset.service - name: enable zomboid world reset path unit become: true become_user: "{{ podman_user }}" ansible.builtin.systemd: name: zomboid-world-reset.path scope: user enabled: true state: started daemon_reload: true