gitea, zomboid updates, ssh key fixes

This commit is contained in:
Bastian de Byl
2025-12-19 10:39:56 -05:00
parent adce3e2dd4
commit 38561cb968
24 changed files with 551 additions and 80 deletions

View File

@@ -1,4 +1,13 @@
---
- name: enable post-quantum key exchange for sshd
become: true
ansible.builtin.template:
src: sshd-pq-kex.conf.j2
dest: /etc/ssh/sshd_config.d/30-pq-kex.conf
mode: 0600
notify: restart_sshd
tags: security, sshd
- name: ensure sshd disallows passwords
become: true
ansible.builtin.lineinfile:

View File

@@ -0,0 +1,9 @@
# Post-Quantum Key Exchange Algorithm
# Managed by Ansible - do not edit directly
#
# Enables sntrup761x25519-sha512 (hybrid post-quantum + classical)
# to protect against "store now, decrypt later" attacks
#
# This must be included BEFORE crypto-policies (40-redhat-crypto-policies.conf)
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512

View File

@@ -1,3 +1,8 @@
---
git_user: git
git_home: "/srv/{{ git_user }}"
# Gitea configuration
gitea_debyl_server_name: git.debyl.io
gitea_image: docker.gitea.com/gitea:1.25.2
gitea_db_image: docker.io/library/postgres:14-alpine

View File

@@ -0,0 +1,13 @@
module gitea-ssh-podman 1.0;
require {
type sshd_t;
type container_runtime_exec_t;
type user_home_t;
class file { execute execute_no_trans open read };
class dir { search };
}
# Allow sshd to execute podman for AuthorizedKeysCommand
allow sshd_t container_runtime_exec_t:file { execute execute_no_trans open read };
allow sshd_t user_home_t:dir search;

View File

@@ -15,3 +15,10 @@
tags:
- git
- selinux
- name: restart sshd
become: true
ansible.builtin.systemd:
name: sshd.service
state: restarted
tags: git

View File

@@ -0,0 +1,28 @@
---
# Deploy gitea shim and shell for SSH passthrough
# The shim is called by SSH when authorized_keys command runs
# It forwards gitea commands to the container
- name: create gitea shim script
become: true
ansible.builtin.template:
src: gitea-shim.j2
dest: /usr/local/bin/gitea
mode: 0755
tags: git, gitea
# The shell is used if someone tries to SSH interactively
- name: create gitea-shell script
become: true
ansible.builtin.template:
src: gitea-shell.j2
dest: /usr/local/bin/gitea-shell
mode: 0755
tags: git, gitea
- name: update git user shell to gitea-shell
become: true
ansible.builtin.user:
name: "{{ git_user }}"
shell: /usr/local/bin/gitea-shell
tags: git, gitea

View File

@@ -0,0 +1,90 @@
---
# Deploy Gitea containers using Podman pod
# Create pod for Gitea services
- name: create gitea-debyl pod
become: true
become_user: "{{ git_user }}"
containers.podman.podman_pod:
name: gitea-debyl-pod
state: started
ports:
- "3100:3000"
tags: gitea
# PostgreSQL container in pod
- name: create gitea-debyl-postgres container
become: true
become_user: "{{ git_user }}"
containers.podman.podman_container:
name: gitea-debyl-postgres
image: "{{ gitea_db_image }}"
pod: gitea-debyl-pod
restart_policy: on-failure:3
log_driver: journald
env:
POSTGRES_DB: gitea
POSTGRES_USER: gitea
POSTGRES_PASSWORD: "{{ gitea_debyl_db_pass }}"
volumes:
- "{{ git_home }}/volumes/gitea/psql:/var/lib/postgresql/data"
tags: gitea
# Gitea container in pod
- name: create gitea-debyl container
become: true
become_user: "{{ git_user }}"
containers.podman.podman_container:
name: gitea-debyl
image: "{{ gitea_image }}"
pod: gitea-debyl-pod
restart_policy: on-failure:3
log_driver: journald
env:
USER_UID: "1000"
USER_GID: "1000"
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: "127.0.0.1:5432"
GITEA__database__NAME: gitea
GITEA__database__USER: gitea
GITEA__database__PASSWD: "{{ gitea_debyl_db_pass }}"
GITEA__server__DOMAIN: "{{ gitea_debyl_server_name }}"
GITEA__server__ROOT_URL: "https://{{ gitea_debyl_server_name }}/"
GITEA__server__SSH_DOMAIN: "{{ gitea_debyl_server_name }}"
GITEA__server__START_SSH_SERVER: "false"
GITEA__server__DISABLE_SSH: "false"
GITEA__server__SSH_PORT: "22"
GITEA__security__SECRET_KEY: "{{ gitea_debyl_secret_key }}"
GITEA__security__INTERNAL_TOKEN: "{{ gitea_debyl_internal_token }}"
GITEA__security__INSTALL_LOCK: "true"
GITEA__service__DISABLE_REGISTRATION: "true"
GITEA__service__REQUIRE_SIGNIN_VIEW: "false"
volumes:
- "{{ git_home }}/volumes/gitea/data:/data"
- /etc/localtime:/etc/localtime:ro
tags: gitea
# Generate systemd service for the pod
- name: create systemd job for gitea-debyl-pod
become: true
become_user: "{{ git_user }}"
ansible.builtin.shell: |
podman generate systemd --name gitea-debyl-pod --files --new
mv pod-gitea-debyl-pod.service {{ git_home }}/.config/systemd/user/
mv container-gitea-debyl-postgres.service {{ git_home }}/.config/systemd/user/
mv container-gitea-debyl.service {{ git_home }}/.config/systemd/user/
args:
chdir: "{{ git_home }}"
changed_when: false
tags: gitea
- name: enable gitea-debyl-pod service
become: true
become_user: "{{ git_user }}"
ansible.builtin.systemd:
name: pod-gitea-debyl-pod.service
daemon_reload: true
enabled: true
state: started
scope: user
tags: gitea

View File

@@ -1,4 +1,10 @@
---
- import_tasks: user.yml
- import_tasks: systemd.yml
- import_tasks: podman.yml
- import_tasks: gitea-shell.yml
- import_tasks: sshd.yml
- import_tasks: selinux.yml
- import_tasks: selinux-podman.yml
- import_tasks: gitea.yml
# git-daemon no longer needed - commented out
# - import_tasks: systemd.yml

View File

@@ -0,0 +1,80 @@
---
# Rootless Podman setup for git user
# Enables running Gitea containers under the git user
# Enable lingering for systemd user services
- name: check if git user lingering enabled
become: true
ansible.builtin.stat:
path: "/var/lib/systemd/linger/{{ git_user }}"
register: git_user_lingering
tags: git, gitea
- name: enable git user lingering
become: true
ansible.builtin.command: |
loginctl enable-linger {{ git_user }}
when: not git_user_lingering.stat.exists
tags: git, gitea
# Set ulimits for container operations
- name: set ulimits for git user
become: true
community.general.pam_limits:
domain: "{{ git_user }}"
limit_type: "{{ item.type }}"
limit_item: "{{ item.name }}"
value: "{{ item.value }}"
loop:
- { name: memlock, type: soft, value: "unlimited" }
- { name: memlock, type: hard, value: "unlimited" }
- { name: nofile, type: soft, value: 39693561 }
- { name: nofile, type: hard, value: 39693561 }
tags: git, gitea
# Create container directories
- name: create git podman directories
become: true
become_user: "{{ git_user }}"
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: 0755
loop:
- "{{ git_home }}/.config/systemd/user"
- "{{ git_home }}/volumes"
- "{{ git_home }}/volumes/gitea"
- "{{ git_home }}/volumes/gitea/data"
# NOTE: psql directory is created by PostgreSQL container with container user ownership
notify: restorecon git
tags: git, gitea
# SELinux context for container volumes
- name: selinux context for git container volumes
become: true
community.general.sefcontext:
target: "{{ git_home }}/volumes(/.*)?"
setype: container_file_t
state: present
notify: restorecon git
tags: git, gitea, selinux
# Enable podman socket for SSH key lookup via AuthorizedKeysCommand
- name: enable podman socket for git user
become: true
become_user: "{{ git_user }}"
ansible.builtin.systemd:
name: podman.socket
enabled: true
state: started
scope: user
tags: git, gitea
# Fetch subuid for volume permissions
- name: fetch subuid of {{ git_user }}
become: true
changed_when: false
ansible.builtin.shell: |
set -o pipefail && cat /etc/subuid | awk -F':' '/{{ git_user }}/{ print $2 }' | head -n 1
register: git_subuid
tags: always

View File

@@ -0,0 +1,21 @@
---
# SELinux policy for SSH + Podman integration
- name: copy gitea SELinux policy module
become: true
ansible.builtin.copy:
src: gitea-ssh-podman.te
dest: /tmp/gitea-ssh-podman.te
mode: 0644
register: selinux_policy
tags: git, gitea, selinux
- name: compile and install gitea SELinux policy
become: true
ansible.builtin.shell: |
cd /tmp
checkmodule -M -m -o gitea-ssh-podman.mod gitea-ssh-podman.te
semodule_package -o gitea-ssh-podman.pp -m gitea-ssh-podman.mod
semodule -i gitea-ssh-podman.pp
when: selinux_policy.changed
tags: git, gitea, selinux

View File

@@ -0,0 +1,19 @@
---
# Configure SSH AuthorizedKeysCommand for Gitea
- name: create gitea-authorized-keys script
become: true
ansible.builtin.template:
src: gitea-authorized-keys.j2
dest: /usr/local/bin/gitea-authorized-keys
mode: 0755
tags: git, gitea
- name: deploy sshd gitea configuration
become: true
ansible.builtin.template:
src: sshd-gitea.conf.j2
dest: /etc/ssh/sshd_config.d/50-gitea.conf
mode: 0644
notify: restart sshd
tags: git, gitea

View File

@@ -0,0 +1,12 @@
#!/bin/sh
# Query Gitea for SSH authorized keys
# Managed by Ansible - do not edit directly
# Arguments: %u (username) %t (key type) %k (key blob)
# Use podman remote to connect via socket (avoids rootless pause process issues)
export CONTAINER_HOST=unix:///run/user/1001/podman/podman.sock
/usr/bin/podman --remote exec -i --user 1000 gitea-debyl \
/usr/local/bin/gitea keys \
-c /data/gitea/conf/app.ini \
-e git -u "$1" -t "$2" -k "$3" 2>/dev/null

View File

@@ -0,0 +1,27 @@
#!/bin/sh
# Gitea SSH shell - forwards commands to Gitea container
# Managed by Ansible - do not edit directly
#
# When sshd runs a forced command from authorized_keys, it invokes:
# <user-shell> -c "<forced-command>"
# The forced command is: /usr/local/bin/gitea --config=... serv key-<id>
# SSH_ORIGINAL_COMMAND contains the client's requested command (e.g., git-upload-pack)
# Use podman remote to connect via socket (avoids rootless pause process issues)
export CONTAINER_HOST=unix:///run/user/1001/podman/podman.sock
if [ "$1" = "-c" ] && [ -n "$2" ]; then
# sshd invoked us with -c "command" - execute the command
# The command is: /usr/local/bin/gitea --config=... serv key-<id>
exec $2
elif [ -n "$SSH_ORIGINAL_COMMAND" ]; then
# Direct invocation with SSH_ORIGINAL_COMMAND (shouldn't happen normally)
echo "Interactive shell is disabled."
echo "Use: git clone git@{{ gitea_debyl_server_name }}:<owner>/<repo>.git"
exit 1
else
# Interactive login attempt
echo "Interactive shell is disabled."
echo "Use: git clone git@{{ gitea_debyl_server_name }}:<owner>/<repo>.git"
exit 1
fi

View File

@@ -0,0 +1,15 @@
#!/bin/sh
# Gitea shim - forwards gitea commands to the container
# Managed by Ansible - do not edit directly
#
# This script is called when sshd executes the forced command from authorized_keys:
# /usr/local/bin/gitea --config=/data/gitea/conf/app.ini serv key-<id>
#
# SSH_ORIGINAL_COMMAND contains the client's git command (e.g., git-upload-pack <repo>)
# Use podman remote to connect via socket (avoids rootless pause process issues)
export CONTAINER_HOST=unix:///run/user/1001/podman/podman.sock
exec /usr/bin/podman --remote exec -i --user 1000 \
--env SSH_ORIGINAL_COMMAND="$SSH_ORIGINAL_COMMAND" \
gitea-debyl /usr/local/bin/gitea "$@"

View File

@@ -0,0 +1,7 @@
# Gitea SSH Key Authentication
# Managed by Ansible - do not edit directly
Match User {{ git_user }}
AuthorizedKeysFile none
AuthorizedKeysCommandUser {{ git_user }}
AuthorizedKeysCommand /usr/local/bin/gitea-authorized-keys %u %t %k

View File

@@ -16,6 +16,28 @@ partsy_path: "{{ podman_volumes }}/partsy"
photos_path: "{{ podman_volumes }}/photos"
uptime_kuma_path: "{{ podman_volumes }}/uptime-kuma"
zomboid_path: "{{ podman_volumes }}/zomboid"
# Zomboid server mode: 'vanilla' or 'modded'
zomboid_server_mode: modded
# Zomboid Discord integration (channel for /all chat relay)
zomboid_discord_channel_id: "1306000323642654751"
# Zomboid RCON port for remote administration
zomboid_rcon_port: "27015"
# Server names for each mode
zomboid_server_names:
vanilla: zomboid
modded: moddedjoboid
# Mod configuration for modded server (deduplicated)
zomboid_mods:
workshop_items: >-
3403870858;3171167894;3330403100;2409333430;3073430075;3379334330;3110913021;3366300557;3034636011;3409287192;3005903549;3161951724;3413704851;3413706334;3287727378;3226885926;2625625421;3418252689;3418253716;3152529790;2478247379;2942793445;2991201484;2913633066;2873290424;3428008364;3253385114;2846036306;2642541073;3435796523;3008795514;3447272250;3026723485;2900580391;2937786633;2870394916;3292659291;2969343830;2566953935;2962175696;3196180339;3258343790;3346905070;3320947974;3478633453;2952802178;3001592312;3052360250;3490370700;2932547723;2805630347;3504401781;2772575623;3110911330;3088951320;3213391371;2932549988;3041122351;2971246021;3539691958;3315443103;2886832257;2886832936;2886833398;2811383142;2799152995;3248388837;3566868353;3570973322;2897390033;3592777775;3596903773;3601417745;3614034284;3577903007;3480990544;3602388131;2463499011;3407042038;3405178154;3402493701;3402812859;3402491515;3430172149;3543229299;3616536783;3431734923;3429790870;2850935956;3307376332;3397182976;3432928943;3610005735;3540297822;3422418897;3426448380;3579640010;3389448389;3393821407;3044705007;2866258937;2544353492;3490188370;3508537032;3451167732;3461263912;2903771337
# Build 42 requires backslash prefix for each mod ID
mod_ids: >-
\LifestyleHobbies;\damnlib;\KI5trailers;\91range;\93fordF350;\82porsche911;\90bmwE30;\91fordLTD;\89dodgeCaravan;\84jeepXJ;\63beetle;\76chevyKseries;\85chevyCaprice;\85pontiacParisienne;\92jeepYJ;\92jeepYJJP18;\87buickRegal;\isoContainers;\85buickLeSabre;\85oldsmobileDelta88;\93chevySuburban;\93chevySuburbanExpanded;\67commando;\90pierceArrow;\69camaro;\70barracuda;\70dodge;\86chevyCUCV;\81deloreanDMC12;\81deloreanDMC12BTTF;\92nissanGTR;\92amgeneralM998;\88toyotaHilux;\91geoMetro;\66pontiacLeMans;\67gt500;\49powerWagon;\69mini;\69mini_ItalianJob;\69mini_MrBean;\69mini_PitbullSpecial;\86fordE150;\86fordE150dnd;\86fordE150mm;\86fordE150pd;\86fordE150expanded;\89volvo200;\93fordElgin;\86oshkoshP19A;\92fordCVPI;\87chevySuburban;\68firebird;\77firebird;\82firebird;\82firebirdKITT;\04vwTouran;\90fordF350ambulance;\93mustangSSP;\87toyotaMR2;\73fordFalcon;\73fordFalconPS;\93townCar;\84merc;\91nissan240sx;\59meteor;\ECTO1;\87fordB700;\93fordTaurus;\75grandPrix;\89trooper;\63Type2Van;\99fordCVPI;\91fordRanger;\98stagea;\82jeepJ10;\82jeepJ10t;\88chevyS10;\89fordBronco;\83amgeneralM923;\78amgeneralM35A2;\78amgeneralM35A2extra;\78amgeneralM49A2C;\78amgeneralM50A3;\78amgeneralM62;\80manKat1;\65banshee;\89defender;\97bushmaster;\84cadillacDeVille;\84buickElectra;\84oldsmobile98;\85chevyStepVan;\85chevyStepVanexpanded;\VanillaFoodsExpanded;\Constown42;\Greenleaf B42 version;\42Grapeseed;\ATA_Jeep;\ATA_Jeep_x10;\ATA_Jeep_x2;\ATA_Jeep_x4;\ATA_Mustang;\ATA_Mustang_x2;\ATA_Mustang_x4;\autotsartrailers;\ATA_Bus;\tsarslib;\flipvehicleplustrailer;\PROJECTRVInterior42;\TombWardrobeALT;\TombWardrobeALTVanilla;\TombBody;\TombBodyCustom;\TombBodyTex;\TombBodyTexDOLL;\TombBodyTexNUDE;\SM4BootsExpandedB42;\SM4BootsExpandedFlatshoes;\GanydeBielovzki's Frockin Splendor!;\RandomClothing;\EFTBP;\AliceGear;\TableSaw;\Ahu'sToolWeapon42.13;\stanks_suicide;\STA_PryOpen;\AutoReload;\DBFaster50;\DBFaster60;\DBFaster70;\DBFaster80;\FixBlowTorchPropaneTank;\MiniHealthPanel;\P4HasBeenRead;\Project_Cook;\NeatUI_Framework;\ModernStatus;\CleanHotBar;\REORDER_THE_HOTBAR
pihole_path: "{{ podman_volumes }}/pihole"
sshpass_cron_path: "{{ podman_volumes }}/sshpass_cron"
caddy_path: "{{ podman_volumes }}/caddy"
@@ -43,6 +65,7 @@ cloud_server_name_io: cloud.debyl.io
home_server_name_io: home.debyl.io
parts_server_name_io: parts.debyl.io
photos_server_name_io: photos.debyl.io
gitea_debyl_server_name: git.debyl.io
# Legacy nginx/ModSecurity configuration removed - Caddy provides built-in security

View File

@@ -106,3 +106,5 @@
include_tasks: podman/systemd-generate.yml
vars:
container_name: awsddns-debyl
# NOTE: git.debyl.io is an ALIAS record to home.debyl.io - no DDNS needed

View File

@@ -23,6 +23,26 @@
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
@@ -40,19 +60,23 @@
restart_policy: on-failure:3
log_driver: journald
env:
SERVER_NAME: zomboid
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:/home/steam/pzserver"
- "{{ zomboid_path }}/data:/home/steam/Zomboid"
- "{{ 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
@@ -70,17 +94,73 @@
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.ini"
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: "zomboid" }
- { key: "DiscordChannelID", value: "{{ zomboid_discord_channel_id }}" }
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 (only 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
# World reset tasks REMOVED - too dangerous to have in automation

View File

@@ -54,9 +54,9 @@
- import_tasks: containers/home/photos.yml
vars:
db_image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
ml_image: ghcr.io/immich-app/immich-machine-learning:v2.3.1
ml_image: ghcr.io/immich-app/immich-machine-learning:v2.4.0
redis_image: docker.io/redis:6.2-alpine@sha256:eaba718fecd1196d88533de7ba49bf903ad33664a92debb24660a922ecd9cac8
image: ghcr.io/immich-app/immich-server:v2.3.1
image: ghcr.io/immich-app/immich-server:v2.4.0
tags: photos
- import_tasks: containers/home/cloud.yml

View File

@@ -243,6 +243,20 @@
}
}
# Gitea - {{ gitea_debyl_server_name }}
{{ gitea_debyl_server_name }} {
import common_headers
reverse_proxy localhost:3100 {
flush_interval -1
}
log {
output file /var/log/caddy/gitea-debyl.log
format json
}
}
# Fulfillr - {{ fulfillr_server_name }} (Static + API with IP restrictions)
{{ fulfillr_server_name }} {
{{ ip_restricted_site() }}

View File

@@ -1,103 +1,89 @@
#!/bin/bash
# Project Zomboid Build 42 Server Entrypoint
# Based on IndifferentBroccoli/projectzomboid-server-docker
set -e
# Configuration
INSTALL_DIR="/project-zomboid"
CONFIG_DIR="/project-zomboid-config"
STEAMCMD="/home/steam/steamcmd/steamcmd.sh"
INSTALL_DIR="/home/steam/pzserver"
DATA_DIR="/home/steam/Zomboid"
SERVER_NAME="${SERVER_NAME:-zomboid}"
MIN_RAM="${MIN_RAM:-8g}"
MAX_RAM="${MAX_RAM:-24g}"
PUID="${PUID:-1000}"
PGID="${PGID:-1000}"
MIN_RAM="${MIN_RAM:-4g}"
MAX_RAM="${MAX_RAM:-8g}"
echo "=== Project Zomboid Build 42 Server ==="
echo "Server Name: ${SERVER_NAME}"
echo "RAM: ${MIN_RAM} - ${MAX_RAM}"
# Fix ownership of mounted volumes (container runs as steam user, UID 1000)
echo "=== Fixing volume permissions ==="
chown -R steam:steam "${INSTALL_DIR}" || true
chown -R steam:steam "${DATA_DIR}" || true
chmod -R 755 "${INSTALL_DIR}" || true
chmod -R 755 "${DATA_DIR}" || true
# Set user permissions (IndifferentBroccoli approach)
echo "=== Setting file permissions ==="
usermod -o -u "${PUID}" steam
groupmod -o -g "${PGID}" steam
chown -R steam:steam "${INSTALL_DIR}" "${CONFIG_DIR}"
# Only chown writable parts of /home/steam (not read-only mounts)
chown steam:steam /home/steam
chown -R steam:steam /home/steam/steamcmd 2>/dev/null || true
chown -R steam:steam /home/steam/Steam 2>/dev/null || true
# Create required subdirectories with correct ownership
mkdir -p "${DATA_DIR}/Server"
mkdir -p "${DATA_DIR}/Saves/Multiplayer"
mkdir -p "${DATA_DIR}/db"
chown -R steam:steam "${DATA_DIR}"
# Ensure steam user has proper home directory setup
export HOME=/home/steam
# Initialize SteamCMD if needed (creates config directories)
if [ ! -d "/home/steam/Steam" ]; then
echo "=== Initializing SteamCMD ==="
su -c "${STEAMCMD} +quit" steam || true
fi
# Create required directories
mkdir -p "${CONFIG_DIR}/Server"
mkdir -p "${CONFIG_DIR}/Saves"
mkdir -p "${CONFIG_DIR}/db"
# Update/Install PZ dedicated server with Build 42 unstable branch
if [ "${AUTO_UPDATE:-true}" = "true" ]; then
echo "=== Updating Project Zomboid Server (Build 42 unstable) ==="
# Run steamcmd as steam user with proper quoting for beta flag
su -c "${STEAMCMD} +force_install_dir ${INSTALL_DIR} +login anonymous +app_update 380870 -beta unstable validate +quit" steam
su -c "${STEAMCMD} +runscript /home/steam/install.scmd" steam
echo "=== Update complete ==="
fi
# Ensure data directories exist (created earlier with correct permissions)
# Configure JVM memory settings in ProjectZomboid64.json (Build 42 uses JSON config)
configure_memory() {
local json_file="${INSTALL_DIR}/ProjectZomboid64.json"
# Configure server settings on first run
SERVER_INI="${DATA_DIR}/Server/${SERVER_NAME}.ini"
if [ ! -f "${SERVER_INI}" ]; then
echo "=== First run detected, server will generate default config ==="
fi
if [ ! -f "$json_file" ]; then
echo "=== ProjectZomboid64.json not found, skipping memory config ==="
return 0
fi
# Handle admin password for first run
# PZ requires interactive password input on first run, so we create a db file
ADMIN_DB="${DATA_DIR}/db/${SERVER_NAME}.db"
if [ ! -f "${ADMIN_DB}" ] && [ -n "${ADMIN_PASSWORD}" ]; then
echo "=== Setting up admin account ==="
mkdir -p "${DATA_DIR}/db"
# The server will prompt for password on first run
# We'll use expect-like behavior or let it use defaults
fi
echo "=== Configuring JVM memory: Xms=${MIN_RAM}, Xmx=${MAX_RAM} ==="
# Modify memory settings in ProjectZomboid64.json (Build 42 uses JSON config)
PZ_JSON="${INSTALL_DIR}/ProjectZomboid64.json"
if [ -f "${PZ_JSON}" ]; then
echo "=== Setting JVM memory: Xms=${MIN_RAM}, Xmx=${MAX_RAM} ==="
# Add -Xms if not present, otherwise update it
if grep -q "\-Xms" "${PZ_JSON}"; then
sed -i "s/-Xms[0-9]*[gGmM]*/-Xms${MIN_RAM}/g" "${PZ_JSON}"
# Update Xmx
sed -i "s/-Xmx[0-9]*[gGmM]*/-Xmx${MAX_RAM}/g" "$json_file"
# Update or add Xms
if grep -q "\-Xms" "$json_file"; then
sed -i "s/-Xms[0-9]*[gGmM]*/-Xms${MIN_RAM}/g" "$json_file"
else
# Insert -Xms before -Xmx
sed -i "s/\"-Xmx/\"-Xms${MIN_RAM}\",\n\t\t\"-Xmx/g" "${PZ_JSON}"
sed -i "s/\"-Xmx/\"-Xms${MIN_RAM}\",\n\t\t\"-Xmx/g" "$json_file"
fi
sed -i "s/-Xmx[0-9]*[gGmM]*/-Xmx${MAX_RAM}/g" "${PZ_JSON}"
echo "=== Memory configuration complete ==="
}
configure_memory
# Check if first run (no admin DB)
ADMIN_DB="${CONFIG_DIR}/db/${SERVER_NAME}.db"
# Build server arguments
# Note: -modfolders is NOT used - mods are configured via INI only
# Reference: IndifferentBroccoli/projectzomboid-server-docker
SERVER_ARGS="-cachedir=${CONFIG_DIR} -servername ${SERVER_NAME}"
# Add admin password for first run
if [ ! -f "${ADMIN_DB}" ] && [ -n "${ADMIN_PASSWORD}" ]; then
echo "=== First run: setting admin password ==="
SERVER_ARGS="${SERVER_ARGS} -adminpassword ${ADMIN_PASSWORD}"
fi
# If server password is set, we'll need to configure it in the ini after first run
# For now, store it for later configuration
if [ -n "${SERVER_PASSWORD}" ]; then
echo "${SERVER_PASSWORD}" > "${DATA_DIR}/.server_password"
fi
# Note: Server password is set via INI file, not command line args
if [ -n "${ADMIN_PASSWORD}" ]; then
echo "${ADMIN_PASSWORD}" > "${DATA_DIR}/.admin_password"
fi
# Change to install directory and start server
# Start server
cd "${INSTALL_DIR}"
echo "=== Starting Project Zomboid Server ==="
echo "Connect to: home.bdebyl.net:16261"
# Start server - on first run this will prompt for admin password
# We handle this by providing input via stdin if password file exists
if [ -f "${DATA_DIR}/.admin_password" ] && [ ! -f "${ADMIN_DB}" ]; then
# First run with admin password
ADMIN_PASS=$(cat "${DATA_DIR}/.admin_password")
echo "=== First run: setting admin password ==="
printf "%s\n%s\n" "${ADMIN_PASS}" "${ADMIN_PASS}" | su -c "bash start-server.sh -servername ${SERVER_NAME}" steam
else
# Normal run
exec su -c "bash start-server.sh -servername ${SERVER_NAME}" steam
fi
exec su -c "export LD_LIBRARY_PATH=${INSTALL_DIR}/jre64/lib:\${LD_LIBRARY_PATH} && ./start-server.sh ${SERVER_ARGS}" steam

View File

@@ -0,0 +1,18 @@
// SteamCMD script for Project Zomboid Server installation
// Based on IndifferentBroccoli/projectzomboid-server-docker
// Do not shutdown on a failed command
@ShutdownOnFailedCommand 0
// No password prompt as this is unattended
@NoPromptForPassword 1
// Set the game installation directory
force_install_dir /project-zomboid
login anonymous
// Install/Update the Project Zomboid Dedicated Server - Unstable Branch (Build 42)
app_update 380870 -beta unstable validate
quit

Binary file not shown.