Skip to content

feat: add UE4SS mod loader & Steam workshop mod loader (Wine) (WIP) - #914

Draft
MusclePr wants to merge 16 commits into
thijsvanloef:mainfrom
MusclePr:feature/SupportsMods
Draft

feat: add UE4SS mod loader & Steam workshop mod loader (Wine) (WIP)#914
MusclePr wants to merge 16 commits into
thijsvanloef:mainfrom
MusclePr:feature/SupportsMods

Conversation

@MusclePr

@MusclePr MusclePr commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Context

(Work In Progress)

  • We can toggle Windows emulation (Wine) or Linux native by image tag.
    • thijsvanloef/palworld-server-docker:windows ... Wine (amd64)
    • thijsvanloef/palworld-server-docker:latest ... Linux (amd64, arm64)
  • Enable running PalServer.exe on Wine.
  • Enable downloading from the Steam Workshop using the Mod ID.
  • Enable the use of experimental UE4SS.
  • Verify that mods can actually be used via examples/mods.
  • Move Steam login credentials to a persistent volume.
    • /home/steam/Steam has been changed to a symbolic link to /palworld/.steam.
    • /palworld/.steam is the actual location of /home/steam/Steam.
  • Add more mods for test.
  • To ensure compatibility with UE4SS Linux, align with the same environment variables.
    Follow the script that has already been implemented.
    The official docs of Installing Mods on a Server is not behaving as described.
  • Use MOD_ENABLED instead of UE4SS_EXPERIMENTAL_INSTALL.
    • True ... UE4SS experimental and PalSchema are used by default.
    • False ... System will withdraw all mods and add -NoMod to the server startup options.
  • I will add MOD_UPDATE_ON_BOOT and MOD_DEBUG to the documentation.
  • Merge important Fixed the CAP_NET_RAW capabilities were not inherited. #928

Choices

I chose Wine to make all mods available.

Test instructions

Prepaire subscribe in steam workshop.

  1. Login steam workshop.
  2. Subscribe UE4SS Exp.
  3. Subscribe PalSchema.
  4. Subscribe Creative Menu.

Prepaire Palworld client

  1. Launch PalWorld client.
  2. Options>Mod>Enable.
  3. Enable Creative Menu Mods.
  4. Save to reboot client.

Prepaire Server

  1. Build Image. docker compose -f windows.yaml build
  2. cd examples/mods
  3. Edit .env
    STEAM_USERNAME=<your steam username>
    
  4. Login once. ./save-login-credential.sh
  5. docker compose down

Test step

  1. Start server. docker compose up
  2. Login to Dedicated Server from client.
  3. Enter chat command /AdminPassword adminPasswordHere
  4. Press F1 to open Creative Menu.

Checklist before requesting a review

  • I have performed a self-review/test of my code
  • I've added documentation about this change to the docs.
  • I've separating the images to avoid including breaking changes.
  • My changes do not violate linting rules.

AI Usage Disclosure

Please read the AI Usage Guidelines for Contributors before submitting this PR.
If you used an AI tool to assist in writing or refactoring code for this PR, please disclose it below.

  • I used GitHub Copilot to assist in modifications for this PR.
    I have manually reviewed the generated code and tested the container build locally.

@MusclePr
MusclePr force-pushed the feature/SupportsMods branch 17 times, most recently from afd30c4 to 5b6c337 Compare August 6, 2026 08:49
@MusclePr
MusclePr force-pushed the feature/SupportsMods branch 4 times, most recently from f3aa448 to 9d66863 Compare August 7, 2026 08:16
@gammamonbiscuit

gammamonbiscuit commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Edit 1: Works after disabling LOG_FILTER_ENABLED


It seems the container would stuck at starting after auto restart or docker compose restart

Debug log stucks here:

++ printf %s '****EXECUTING USERMOD****'
+ formatted_message='****EXECUTING USERMOD****'
+ '[' -p /home/steam/server/.palserver_log_fifo ']'
+ timeout 1s echo 'LOG:****EXECUTING USERMOD****'

docker top:

UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                2196482             2196457             0                   15:42               ?                   00:00:00            /bin/bash /home/steam/server/init.sh
root                2196558             2196482             0                   15:42               ?                   00:00:00            /bin/bash /home/steam/server/init.sh
compose
name: test
x-logging: &logging-setting
  logging:
    driver: "json-file"
    options:
      max-file: "5"
      max-size: "3m"

services:
  palworld-test:
    image: thijsvanloef/palworld-server-docker:windows
    container_name: palworld-test
    ports:
      - 60000:60000/udp  # Game port, default 8211
      #- 60001:60001/udp  # Steam query, default 27015
      #- 60002:60002/tcp  # REST API, default 8212
    env_file:
      - $HOME/.volumes/.palworld-test/.env
    volumes:
      - $HOME/.volumes/.palworld-test/data:/palworld/
    <<: *logging-setting
    stop_grace_period: 60s
    networks: [containers]
    mem_limit: 16g
    memswap_limit: 20g
    restart: unless-stopped

networks:
  containers:
    enable_ipv6: false
    ipam:
      driver: default
      config:
        - subnet: 172.20.3.0/24
          gateway: 172.20.3.1
env (without in-game settings part)
########################## WINDOWS SPECIFIC ##########################

# Steam username for downloading the server with a TARGET_MANIFEST_ID.
STEAM_USERNAME=redacted
# Comma-separated Steam Workshop mod IDs to download and deploy.
WORKSHOP_MOD_IDS=3625223587,3625280368
# Cron expression for periodic Workshop mod sync checks. Leave empty to disable periodic checks.
WORKSHOP_MOD_UPDATE_CRON=
# Install latest experimental UE4SS package download and deployment.
UE4SS_EXPERIMENTAL_INSTALL=false
# Download URL for the experimental UE4SS package zip.
UE4SS_EXPERIMENTAL_URL=https://github.com/Okaetsu/RE-UE4SS/releases/download/experimental-palworld/UE4SS-Palworld.zip

########################## SERVER SETTINGS ##########################

# Update/Install the server when the docker container starts (THIS HAS TO BE ENABLED THE FIRST TIME YOU RUN THE CONTAINER)
UPDATE_ON_BOOT=false

# Enables automatic backups
BACKUP_ENABLED=true
# Enables native automatic backups
USE_BACKUP_SAVE_DATA=true
# Delete backups after a certain number of days
DELETE_OLD_BACKUPS=true
# How many days to keep backups
OLD_BACKUP_DAYS=30
# Setting affects frequency of automatic backups.
BACKUP_CRON_EXPRESSION=50 2,14 * * *

# Enables automatic updates
AUTO_UPDATE_ENABLED=false
# Setting affects frequency of automatic updates.
AUTO_UPDATE_CRON_EXPRESSION=0 * * * *
# How long to wait to update the server, after the player were informed. (This will be ignored, if no Players are connected)
AUTO_UPDATE_WARN_MINUTES=30
# Enables automatic reboots
AUTO_REBOOT_ENABLED=true
# Restart the Server even if there are players online.
AUTO_REBOOT_EVEN_IF_PLAYERS_ONLINE=false
# How long to wait to reboot the server, after the player were informed.
AUTO_REBOOT_WARN_MINUTES=5
# Setting affects frequency of automatic updates.
AUTO_REBOOT_CRON_EXPRESSION=* * * * *

# Timezone used for time stamping backup server
TZ=Asia/Hong_Kong
# Max amount of players that are able to join the server
PLAYERS=16
# UDP port that the server will expose
PORT=60000
# The uid of the user the server should run as
PUID=1000
# The gid of the group the server should run as
PGID=1000
# Enables performance-related threading startup arguments (-useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS).
ENABLE_PERF_THREADING_ARGS=true
# Sets -NumberOfWorkerThreadsServer. Leave empty to use the game's default. When using deprecated MULTITHREADING=true, this defaults to all CPUs.
WORKER_THREADS_SERVER=10
# Enables Palworld's built-in recovery for negative DeltaTime through an Engine ini override. This opt-in mitigation does not correct or synchronize the host or VM clock.
PALWORLD_ALLOW_NEGATIVE_DELTA_TIME=false
# Whether or not the server shows up in the community server browser (USE WITH SERVER_PASSWORD)
COMMUNITY=false
# You can manually specify the global IP address of the network on which the server running. If not specified, it will be detected automatically. If it does not work well, try manual configuration.
PUBLIC_IP=209.25.142.27
# You can manually specify the port number of the network on which the server running. If not specified, it will be detected automatically. If it does not work well, try manual configuration.
PUBLIC_PORT=1108
# A name for your server
SERVER_NAME=Biscuit Test Server
# Your server Description
SERVER_DESCRIPTION=FOR TESTING ONLY
# Secure your community server with a password
SERVER_PASSWORD=
# Secure administration access in the server with a password
ADMIN_PASSWORD=redacted
# Enable REST API for the palworld server
REST_API_ENABLED=true
# REST API port to connect to
REST_API_PORT=60002
# Enable RCON for the Palworld server
RCON_ENABLED=false
# RCON port to connect to
RCON_PORT=60003
# Query port used to communicate with Steam servers
QUERY_PORT=60001
# Enables automatic pause (with ENABLE_PLAYER_LOGGING=true and REST_API_ENABLED=true required.)
AUTO_PAUSE_ENABLED=true
# Default 180 (seconds) describes the time between the last client disconnect and the pausing of the process (read as timeout established)
AUTO_PAUSE_TIMEOUT_EST=180
# Enable auto-pause logging
AUTO_PAUSE_LOG=true
# Enable auto-pause debug logging
AUTO_PAUSE_DEBUG=false
# Enables Logging and announcing when players join and leave (with REST_API_ENABLED=true required.)
ENABLE_PLAYER_LOGGING=true
# Polling period (in seconds) to check for players who have joined or left
PLAYER_LOGGING_POLL_PERIOD=5
# Enable filter to reduce duplicated log lines
LOG_FILTER_ENABLED=true
# Minimum container log level to emit (DEBUG < INFO < WARN < ERROR; SUCCESS/ACTION are treated as INFO) (DEBUG/INFO/WARN/ERROR/OFF)
LOG_LEVEL=DEBUG
# Configure log format type (json/logfmt/colored/plain/default)
LOG_FORMAT_TYPE=default
# Uses DepotDownloader to download game server files instead of steamcmd. This will help hosts incompatible with steamcmd (e.g. M-series Mac)
USE_DEPOT_DOWNLOADER=false
# Installs the latest Beta version of the Palworld dedicated server
INSTALL_BETA_INSIDER=false

@MusclePr

MusclePr commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

@gammamonbiscuit
Thanks for the review.
I have confirmed that docker compose restart fails, and not just on this branch.
Fortunately, a fix has already been implemented in #928, so I plan to merge it via a rebase.

@gammamonbiscuit

Copy link
Copy Markdown
Contributor

Cool, thanks!
Another question: Is there any way I can quickly disable mod update, if not currently, where to edit the script to do so so that I can do some manual tests?

Reason behind this question is that, we tried to test this mod yesterday, but it didn't work, after some debugging, we think it is not installed correctly, but I haven't tested it so this is just a guess.
https://steamcommunity.com/sharedfiles/filedetails/?id=3765490028

Here's what steam downloaded:

~/.volumes/.palworld-test/data/.steam/steamapps/workshop/content/1623730/3765490028 > tree -a .
.
├── Info.json
├── Scripts
│   ├── config.lua
│   └── main.lua
├── thumbnail.png
└── .workshop.json

2 directories, 5 files

And here's what the script installed:

~/.volumes/.palworld-test/data/Pal/Binaries/Win64/ue4ss/Mods/LevelLock > tree -a .
.
├── config.lua
└── main.lua

1 directory, 2 files

After comparing other player's local install, we think the lua scripts should be stayed in the Scripts folder, again, not yet tested.

@MusclePr

MusclePr commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Added the NOMODS environment variable; when set to true, -NoMods is added to the startup arguments.
However, the mods are merely disabled; they are still installed.

@gammamonbiscuit

Copy link
Copy Markdown
Contributor

Now restart works with LOG_FILTER_ENABLED enabled.
But for some reason I don't see any console output from the server process, with or without that setting, this is already happened before the recent commit but I forgot to mention.


NOMODS isn't exactly what I need at this moment soooo I stole this patching trick from your other post...
When UPDATE_MODS_ON_BOOT is disabled, mod update is disabled and won't overwrite UE4SS and mods with brand new copies.

    entrypoint:
      - /bin/bash
      - -c
      - |
        #!/bin/bash
        sed -i -r -z 's/if\s(\[.*?\])(;\sthen)(\n.*?\n.*?mods\-update)/if \1 \&\& [ "$${UPDATE_MODS_ON_BOOT,,}" = true ]\2\3/' ./start.sh
        exec ./init.sh

After more tests, it seems UE4SS (I use workshop version) is looking for Pal\Binaries\Win64\Mods whilst your designed path is Pal\Binaries\Win64\ue4ss\Mods.

[2026-08-10 15:26:31.1437216] Mods directory doesn't exist, skipping: Z:\palworld\Pal\Binaries\Win64\Mods

I have to disable mod update, and edit UE4SS-settings.ini to provide the mod folder path:

ModsFolderPath = Z:\palworld\Pal\Binaries\Win64\ue4ss\Mods

And move LevelLock's files back into Scripts folder, now the mod loads and works in-game as expected.

> cat ~/.volumes/.palworld-test/data/Pal/Binaries/Win64/UE4SS.log | grep "LevelLock"
[2026-08-10 15:32:09.2439370] Starting Lua mod 'LevelLock'
[2026-08-10 15:32:09.2465928] [Lua] [LevelLock] Progress file: Z:\palworld\Pal\Saved\LevelLock-progress.txt (the game's own Saved folder)
[2026-08-10 15:32:09.2493960] [Lua] [LevelLock] v2.3.3 loaded. Waiting for world...

PalSchema would crash on launch tho.

@gammamonbiscuit

Copy link
Copy Markdown
Contributor

Thank you for the quick updates! I added more mods to test, log looks fine but I need some more time to test to see if they actually work since they target different aspects of the game.

Currently my last problem before I can test it with my prod server is that, I'm not yet able to find a way to get stdout printed like in the linux version. I tried launching /palworld/Pal/Binaries/Win64/PalServer-Win64-Shipping-Cmd.exe directly instead, log entries did show up but awfully delayed, tried stdbuf -oL and stdbuf -o0, still the same. This is more of a personal request... because I need the log to read chats in real time.

@gammamonbiscuit

gammamonbiscuit commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

During tests, I also noticed that many mods use a config file to let user change settings, which is shipped together and placed next to their main script, which would be deleted during mod update.

UPDATE_MODS_ON_BOOT can prevent this but also prevents all updates which eventually the user must do. So, I think maybe having a filter that stops specific files from being deleted during update would be nice.

Some ideas that comes to my mind:

  1. Still delete files, but let user freeze workshop ids and don't update them.
  2. Add a user editable filter list, don't touch those files at all.
  3. Add a user editable filter list, copy them to a temp location before update, and put them back after.

I think the third one would be the easiest and doesn't affect the current workflow.

@MusclePr
MusclePr force-pushed the feature/SupportsMods branch from 464a05a to f723ebb Compare August 14, 2026 07:48
@MusclePr
MusclePr force-pushed the feature/SupportsMods branch 2 times, most recently from 430dc88 to 9cd8b6b Compare August 14, 2026 11:54
@MusclePr

Copy link
Copy Markdown
Contributor Author

Since user-edited files have their timestamps updated, I utilized this to prevent them from being overwritten or deleted when the mod is updated.

@gammamonbiscuit

Copy link
Copy Markdown
Contributor

Using last edit time is indeed a better idea haha, I'm not experiencing problem on this part yet.


As for the realtime log, it seems it broke both auto pause and log filtering.
(AUTO_PAUSE_TIMEOUT_EST was set to 30 for quicker test here.)

Repeated lines didn't combine.

[2026-08-15 23:59:24] [LOG] REST accessed endpoint /v1/api/players OK
[2026-08-15 23:59:29] [LOG] REST accessed endpoint /v1/api/players OK
[2026-08-15 23:59:34] [LOG] REST accessed endpoint /v1/api/players OK

kill failed

[AUTO PAUSE] Paused. (PID:189
190
191
274)
/home/steam/server/autopause/functions.sh: line 91: kill: 189
[AUTO PAUSE] AUTO_PAUSE_KNOCKD_IF="auto" resolved to: "eth0 lo"
190
191
274: arguments must be process or job IDs

And these, I don't know... I'm not very familiar with how the whole auto pause work.

[2026-08-16 00:00:15] [LOG] REST accessed endpoint /v1/api/players OK
[AUTO PAUSE DEBUG] AP_pushRequest: Resumed by REST_API players.
[AUTO PAUSE DEBUG] AP_waitPullRequest ... time out.
[2026-08-16 00:00:31] [LOG] REST accessed endpoint /v1/api/players OK
[AUTO PAUSE DEBUG] AP_pushRequest: Resumed by REST_API players.
[AUTO PAUSE DEBUG] AP_waitPullRequest ... time out.

Log: log.txt

@MusclePr
MusclePr force-pushed the feature/SupportsMods branch 3 times, most recently from 50b9c98 to f4a293c Compare August 18, 2026 07:43
Comment thread scripts/start.sh Outdated
/home/steam/server/compile-engine.sh || exit
fi

if [ "${platform}" = "windows" ] && isTrue "${UPDATE_MODS_ON_BOOT}"; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old env name UPDATE_MODS_ON_BOOT is still being used here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!
Fixed at 5a54d98

Comment thread scripts/mods/update.sh Outdated
local line
local -r ignore_ue4ss_id="3625223587" # UE4SS workshop mod ID

if [ -n "${WORKSHOP_MOD_IDS:-}" ]; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old env name WORKSHOP_MOD_IDS is still being used here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!
Fixed at 5a54d98

@MusclePr
MusclePr force-pushed the feature/SupportsMods branch 2 times, most recently from 89fac6a to 5a54d98 Compare August 19, 2026 23:40
@gammamonbiscuit

Copy link
Copy Markdown
Contributor

Nice, it is now good enough for my server's use, thank you. I think it is time to get more people to try on this in case I missed something?

Some suggestions...

Document: Mention that disabling mod will uninstall mods, so that the user needs to enable update to reinstall them.

Debug Logging: Maybe add a header like [UE4SS] for ue4ss logs?

nlopez added a commit to nlopez/k8s_home that referenced this pull request Aug 23, 2026
Points at ghcr.io/nlopez/palworld-server-docker built from branch
mods-wine-support (PR thijsvanloef/palworld-server-docker#914), which
runs the server under Wine so Steam Workshop mods can be downloaded
via MOD_IDS. Fixes health-check probes to use the image's own
platform-aware process check instead of the hardcoded Linux process
name, and enables MOD_ENABLED/MOD_UPDATE_ON_BOOT/MOD_IDS in the
configmap for mod IDs 3772992108,3773035536.
@MusclePr
MusclePr force-pushed the feature/SupportsMods branch from 36336b1 to 8a28b9a Compare August 31, 2026 03:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants