Game Hosting home

Valheim Linux Server Mods: Native Linux or Docker

Set up Valheim Linux server mods with the right BepInEx paths, service permissions and persistent Docker mounts.

Difficulty
HardDifficulty Hard
Time to do
12 min
Reading
9 min
Updated
By
Game Hosting team

You will need

  • A Linux host or Docker Engine with permission to manage the server process and its files
  • A stopped, backed-up Valheim server and a list of the mods and versions the crew chose
  • SSH or shell access to inspect service logs, container mounts and file ownership

Valheim Linux server mods need two things to survive: a loader that starts with the dedicated server, and plugin files stored somewhere the next restart or container can still read. The exact path depends on whether you run a native Linux service or a Docker image. This guide covers both and shows how to check the loader and mounts before players return.

Mods are unofficial. Iron Gate says it does not support them and warns that compatibility can change after game updates. Check the mod author’s current instructions, dependencies and client/server role before installing anything. The Valheim mod policy does not promise that a particular package will keep working.

If you want to control every file and update, self-hosting gives you that control along with the work of testing and preserving the setup. If you would rather manage supported mod search, dependencies and profiles from a panel, Game Hosting’s Valheim service documents those controls. Neither route makes an unofficial mod compatible by itself. Start by deciding which side of that maintenance work your crew wants to own.

Choose native Linux or Docker first#

Use a native service when you want direct control of the Linux process, service account and install directory. Use Docker when you want the server to run through a maintained image and can keep its documented configuration mount and install data. The older lloesche/valheim-server project has moved: its maintained successor is community-valheim-tools/valheim-server-docker. Follow the current repository’s variable names and paths instead of copying an old Compose example.

Before changing either setup, write down the Valheim server build, loader pack version, every plugin version, dependencies, and which packages require client installation. Keep this list with your backups. For a crew-level plan that checks the mod roster and client/server roles, use the Valheim multiplayer mod compatibility guide.

Set up Valheim Linux server mods as a native service#

First identify the directory that actually contains valheim_server.x86_64, the launch script and the server’s valheim_server_Data folder. Stop the service and copy the current world and configuration to a separate location. Do not experiment on the only copy of a live world.

The current BepInExPack Valheim instructions provide a dedicated Linux server script named start_server_bepinex.sh. Start from a clean server install, extract the pack, and place the contents of its BepInExPack_Valheim folder in the server root so the script and BepInEx directory are not hidden inside an extra nested folder. The pack’s Linux server steps say to make the script executable, edit its Valheim launch parameters, then run it and look for bootstrap messages:

bash
cd /srv/valheim
chmod u+x start_server_bepinex.sh

Use your actual server root instead of /srv/valheim. Edit the launch parameters in the script to preserve the server name, world, port and password settings you already use. Do not paste a real password into a public script or log. The pack’s manual installation notes include its current file layout and Linux launch procedure; the general BepInEx Linux installation guide covers the loader separately.

Run the server as a dedicated unprivileged account, and point systemd at the same root you used above. A unit can look like this after you replace its paths and service name with your own:

ini
[Unit]
Description=Valheim dedicated server with BepInEx
After=network-online.target
Wants=network-online.target

[Service]
User=valheim
WorkingDirectory=/srv/valheim
ExecStart=/srv/valheim/start_server_bepinex.sh
Restart=on-failure
RestartSec=10
KillSignal=SIGINT
TimeoutStopSec=120

[Install]
WantedBy=multi-user.target

The User must own or be able to read the server files, write the save directory and execute the launcher. The WorkingDirectory matters because launch scripts may resolve files relative to the current directory. Check the unit before enabling it, then inspect both systemd and BepInEx output:

bash
sudo systemd-analyze verify /etc/systemd/system/valheim.service
sudo systemctl daemon-reload
sudo systemctl enable --now valheim.service
sudo systemctl status valheim.service
sudo journalctl -u valheim.service -f

The Valheim pack says successful Linux server startup prints BepInEx bootstrap messages. Its current changelog also describes the chainloader reporting loaded, skipped and failed plugin counts in LogOutput.log. Check for a completed chainloader and review any failed entries. A running process alone does not prove that a plugin loaded. Keep the unit, launcher, world, and plugin configuration outside any directory your server updater replaces; inspect your own updater before assuming it preserves extra files.

Put Docker plugins and configuration on persistent mounts#

The maintained image’s BepInEx documentation says to set BEPINEX=true. It keeps the pack’s settings under /config/bepinex; plugins go in /config/bepinex/plugins and patchers in /config/bepinex/patchers. On startup and install/update, it syncs those files into /opt/valheim/bepinex/BepInEx/.... That is an image-specific mapping, not a universal Docker path.

Keep /config outside the disposable container. You can also persist /opt/valheim to retain the server install and its depot-manifest cache. This excerpt uses the current v1.4.0 image digest observed during testing; confirm the tag and digest again when you intentionally update:

yaml
services:
  valheim:
    image: ghcr.io/community-valheim-tools/valheim-server:1.4.0@sha256:f3ccde9a4e292663cf5096d502ff33cc9617015f6d70b6a9ca0968543f165ef2
    restart: unless-stopped
    environment:
      SERVER_NAME: ${VALHEIM_SERVER_NAME}
      WORLD_NAME: ${VALHEIM_WORLD_NAME}
      SERVER_PASS: ${VALHEIM_SERVER_PASS}
      BEPINEX: "true"
    volumes:
      - ./config:/config
      - ./data:/opt/valheim

The image expects SERVER_PASS to be at least five characters. Keep real credentials in a protected environment file, not in the article’s example or a committed Compose file. Create the host directories before the first start, and keep the same paths when replacing the container. The image’s README documents its current environment variables, volume behavior, update flow and network options; use that source for a complete server deployment.

After first start, copy server-side plugins into the host’s config/bepinex/plugins directory and patchers into config/bepinex/patchers. Do not install them only with docker exec inside the running container: those files may live in its replaceable layer and will not be there when the next container is created. The image syncs from /config one way, so a file removed there can be pruned from the server directory during a later sync. Read the release notes when updating because sync behavior can change.

Check ownership and executable access without broad permissions#

On native Linux, compare the service account with the files it must use:

bash
id valheim
stat -c '%U:%G %a %n' /srv/valheim/start_server_bepinex.sh /srv/valheim/BepInEx

The launcher needs an execute bit, and the service account needs the access required by the server. If not, fix the specific owner or mode based on your install method. Avoid chmod -R 777 and blanket chown -R commands: they hide which path is wrong and can weaken unrelated server files.

For Docker, check the actual mount sources and process identity rather than guessing from the Compose file:

bash
docker inspect --format '{{json .Mounts}}' valheim
docker exec valheim id
docker exec valheim stat -c '%U:%G %a %n' /config /opt/valheim /opt/valheim/bepinex/BepInEx/plugins

Bind mounts use the host path you supplied, so its existence and permissions matter. A named volume is managed by Docker outside the container lifecycle. The Docker bind-mount and volume guides explain the difference. Ownership observed on Docker Desktop with a Windows bind mount can differ from a Linux host; inspect the machine that actually runs your server before changing UID or GID settings.

Verify bootstrap and the server-side plugin you chose#

Start with one server-side plugin and its stated dependencies, not a full mod pack. Read the package manifest and author’s current notes to see whether the plugin is server-side, client-side, or required on both. The server’s BepInEx log can show that a plugin loaded; it cannot prove a client joined, that multiplayer state synchronized, or that gameplay works. Test those claims separately with the exact current client build and mod profile before inviting the crew.

On native Linux, restart through systemd and inspect the same service and BepInEx logs again. For Docker, keep the same image digest and mounts, then recreate only the container:

bash
docker compose stop valheim
docker compose rm -f valheim
docker compose up -d valheim
docker compose logs -f valheim

Do not add -v to routine cleanup. That option can remove named volumes along with containers. Confirm that startup reaches Game server connected, that the BepInEx log reports the expected plugin count without a load failure, and that the chosen plugin and its configuration still exist under /config. Compare their hashes before and after the recreation if you need a repeatable check. A Docker image update is a separate change: back up first, review the new image and mod requirements, then test the new digest before players reconnect.

For either route, check the world directory and plugin configuration before declaring the test complete. Keep an independent backup, and rehearse restoring it to a separate server if the campaign matters. The Valheim Docker setup and restore guide covers the container’s world mounts; this page focuses on where BepInEx files go and how to check that they persist.

For this image’s current /config and /opt/valheim mounts, scheduled updates and backup settings, use the lloesche Valheim Docker reference alongside the image’s latest release notes.

When a plugin stops loading after an update#

Stop before changing permissions or deleting files. Record the server build, image digest or package version, loader version, plugin version and full error. Then check the relevant branch:

  • Native service: verify systemd’s User, WorkingDirectory and ExecStart; confirm the pack files still sit beside the server executable, the launcher is executable, and the service can read the plugin directory.
  • Docker: verify the expected host path is mounted at /config, the plugin is under the image’s documented subdirectory, and the image reports the same digest you intended to run. Check the startup sync and LogOutput.log before changing ownership.
  • Both: compare the plugin’s game-version support and dependencies with the server build. Roll back to the backed-up world, configuration and previously tested versions if the failure followed an update; do not let a second server write to the same world at the same time.

Iron Gate does not guarantee mod compatibility across game updates. A clean loader boot is useful evidence, but it is not a multiplayer acceptance test. Add one change at a time, capture the result, and only then invite players back.

Test scope and sources#

The native Linux check used Valheim dedicated server 1.0.17 and BepInEx 5.4.23.5 / BepInExPack Valheim 5.4.2351 in a disposable WSL2 Ubuntu directory. A user-scoped systemd service ran under christian, used the server root as its working directory, and started start_server_bepinex.sh. A minimal test plugin logged successfully; chainloader reported one loaded plugin and no failures. A graceful stop saved the temporary world. SteamCMD app_update 896660 validate succeeded against the same current server build; the launcher, plugin, configuration and world files remained. On restart the plugin loaded again and the server reached Loading: Done. No newer game build was available during this check, so it does not prove compatibility with a future game update.

The Docker check used community-valheim-tools image v1.4.0 at the pinned digest above, with the same game and BepInEx versions. After container recreation, the image synced the test plugin from /config/bepinex/plugins to its server plugin directory, the plugin logged, and chainloader reported one loaded plugin and no failures. The mounted world and a configuration marker remained. The check ran on Docker Desktop with temporary Windows bind mounts; those paths appeared as root:root inside the container, which may differ on a Linux host. No licensed Valheim client joined, no gameplay or client synchronization was tested, and this smoke test does not establish compatibility of other plugins.

Sources: Iron Gate’s mod policy, BepInEx installation guide, Valheim BepInEx pack, maintained Valheim server image, and Docker storage documentation.

Quest complete

Before you invite your friends, check that the intended world loaded and that each player can connect. If those checks pass, you're ready for a session; if you'd rather not keep a PC on, that's what we're here for.

Quick answers

Will plugins placed only inside a Valheim container survive recreation?

Only if the image syncs them from a persistent mount or they are stored in another persistent volume. Files placed only in a replaceable container layer can disappear when that container is replaced.

Do Windows loader files work unchanged on Linux?

No. Use the Linux instructions and files from the Valheim BepInEx pack. Do not copy Windows-only loader files into a Linux server directory.

Can an automatic image update break a frozen mod profile?

It can. Valheim and unofficial mods may change independently, so back up the world and mod configuration, pin an image, and test a planned update before your crew rejoins.