lloesche/valheim-server

By lloesche

Updated 9 months ago

Valheim dedicated gameserver with automatic update, World backup, BepInEx and ValheimPlus support

Image
198

10M+

lloesche/valheim-server Docker image

Valheim

Valheim Server in a Docker Container (with BepInEx and ValheimPlus support)
This project is hosted at https://github.com/lloesche/valheim-server-docker

Docker Badge

Table of contents

Basic Docker Usage

The name of the Docker image is lloesche/valheim-server.

Volume mount the server config directory to /config within the Docker container.

If you have an existing world on a Windows system you can copy it from e.g.
C:\Users\Lukas\AppData\LocalLow\IronGate\Valheim\worlds to e.g.
$HOME/valheim-server/config/worlds and run the image with $HOME/valheim-server/config volume mounted to /config inside the container. The container directory /opt/valheim contains the downloaded server. It can optionally be volume mounted to avoid having to download the server on each fresh start.

$ mkdir -p $HOME/valheim-server/config/worlds $HOME/valheim-server/data
# copy existing world
$ docker run -d \
    --name valheim-server \
    --cap-add=sys_nice \
    --stop-timeout 120 \
    -p 2456-2457:2456-2457/udp \
    -v $HOME/valheim-server/config:/config \
    -v $HOME/valheim-server/data:/opt/valheim \
    -e SERVER_NAME="My Server" \
    -e WORLD_NAME="Neotopia" \
    -e SERVER_PASS="secret" \
    lloesche/valheim-server

Warning: SERVER_PASS must be at least 5 characters long. Otherwise valheim_server.x86_64 will refuse to start!

A fresh start will take several minutes depending on your Internet connection speed as the container will download the Valheim dedicated server from Steam (~1 GB).

Do not forget to modify WORLD_NAME to reflect the name of your world! For existing worlds that is the filename in the worlds/ folder without the .db/.fwl extension.

If you want to play with friends over the Internet and are behind NAT make sure that UDP ports 2456-2457 are forwarded to the container host. Also ensure they are publicly accessible in any firewall.

There is more info in section Finding Your Server.

For LAN-only play see section Steam Server Favorites & LAN Play

For more deployment options see the Deployment section.

Granting CAP_SYS_NICE to the container is optional. It allows the Steam library that Valheim uses to give itself more CPU cycles. Without it you will see a message Warning: failed to set thread priority in the startup log.

Environment Variables

All variable names and values are case-sensitive!

NameDefaultPurpose
SERVER_NAMEMy ServerName that will be shown in the server browser
SERVER_PORT2456UDP start port that the server will listen on
WORLD_NAMEDedicatedName of the world without .db/.fwl file extension
SERVER_PASSsecretPassword for logging into the server - min. 5 characters!
SERVER_PUBLICtrueWhether the server should be listed in the server browser (true) or not (false)
SERVER_ARGSAdditional Valheim server CLI arguments
ADMINLIST_IDSSpace separated list of admin SteamIDs. Overrides any existing adminlist.txt entries!
BANNEDLIST_IDSSpace separated list of banned SteamIDs. Overrides any existing bannedlist.txt entries!
PERMITTEDLIST_IDSSpace separated list of whitelisted SteamIDs. Overrides any existing permittedlist.txt entries!
UPDATE_CRON*/15 * * * *Cron schedule for update checks (disabled if set to an empty string or if the legacy UPDATE_INTERVAL is set)
UPDATE_IF_IDLEtrueOnly run update check if no players are connected to the server (true or false)
RESTART_CRON0 5 * * *Cron schedule for server restarts (disabled if set to an empty string)
RESTART_IF_IDLEtrueOnly run daily restart if no players are connected to the server (true or false)
TZEtc/UTCContainer time zone
BACKUPStrueWhether the server should create periodic backups (true or false)
BACKUPS_CRON0 * * * *Cron schedule for world backups (disabled if set to an empty string or if the legacy BACKUPS_INTERVAL is set)
BACKUPS_DIRECTORY/config/backupsPath to the backups directory
BACKUPS_MAX_AGE3Age in days after which old backups are flushed
BACKUPS_MAX_COUNT0Maximum number of backups kept, 0 means infinity
BACKUPS_IF_IDLEtrueBackup even when no players have been connected for a while
BACKUPS_IDLE_GRACE_PERIOD3600Grace period in seconds after the last player has disconnected in which we will still create backups when BACKUPS_IF_IDLE=false
PERMISSIONS_UMASK022Umask to use for backups, config files and directories
STEAMCMD_ARGSvalidateAdditional steamcmd CLI arguments
VALHEIM_PLUSfalseWhether ValheimPlus mod should be loaded (config in /config/valheimplus, additional plugins in /config/valheimplus/plugins). Can not be used together with BEPINEX.
BEPINEXfalseWhether BepInExPack Valheim mod should be loaded (config in /config/bepinex, plugins in /config/bepinex/plugins). Can not be used together with VALHEIM_PLUS.
SUPERVISOR_HTTPfalseTurn on supervisor's http server
SUPERVISOR_HTTP_PORT9001Set supervisor's http server port
SUPERVISOR_HTTP_USERadminSupervisor http server username
SUPERVISOR_HTTP_PASSSupervisor http server password
STATUS_HTTPfalseTurn on the status http server. Only useful on public servers (SERVER_PUBLIC=true).
STATUS_HTTP_PORT80Status http server tcp port
STATUS_HTTP_CONF/config/httpd.confPath to the busybox httpd config
STATUS_HTTP_HTDOCS/opt/valheim/htdocsPath to the status httpd htdocs where status.json is written
SYSLOG_REMOTE_HOSTRemote syslog host or IP to send logs to
SYSLOG_REMOTE_PORT514Remote syslog UDP port to send logs to
SYSLOG_REMOTE_AND_LOCALtrueWhen sending logs to a remote syslog server also log local
PUID0UID to run valheim-server as
PGID0GID to run valheim-server as

There are a few undocumented environment variables that could break things if configured wrong. They can be found in defaults.

Log filters

Valheim server by default logs a lot of noise. These env variables allow users to remove unwanted lines from the log.

PrefixDefaultPurpose
VALHEIM_LOG_FILTER_EMPTYtrueFilter empty log lines
VALHEIM_LOG_FILTER_UTF8trueFilter invalid UTF-8 characters
VALHEIM_LOG_FILTER_MATCHFilter log lines exactly matching
VALHEIM_LOG_FILTER_STARTSWITH(Filename:Filter log lines starting with
VALHEIM_LOG_FILTER_ENDSWITHFilter log lines ending with
VALHEIM_LOG_FILTER_CONTAINSFilter log lines containing
VALHEIM_LOG_FILTER_REGEXPFilter log lines matching regexp

The default filter removes:

  • Empty log lines
  • Log lines consisting of a single space (wtf?)
  • A repeating line saying (Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 35)
  • Lines flodding the log with Assertion Failed warnings on packet processing timeouts (See #104)
  • If ValheimPlus is turned on lines starting with Fallback handler could not load library
Log filter event hooks

If an environment variable prefixed with ON_ exists for an identically named log filter, instead of removing the log line the contents of the variable will be executed when the filter matches with the log line piped on stdin.

PrefixPurpose
ON_VALHEIM_LOG_FILTER_MATCHRun command hook on log lines exactly matching
ON_VALHEIM_LOG_FILTER_STARTSWITHRun command hook on log lines starting with
ON_VALHEIM_LOG_FILTER_ENDSWITHRun command hook on log lines ending with
ON_VALHEIM_LOG_FILTER_CONTAINSRun command hook on log lines containing
ON_VALHEIM_LOG_FILTER_REGEXPRun command hook on regexp match

All environment variables except for VALHEIM_LOG_FILTER_EMPTY and VALHEIM_LOG_FILTER_UTF8 are prefixes. Meaning you can define multiple matches like so:

-e VALHEIM_LOG_FILTER_STARTSWITH=foo \
-e VALHEIM_LOG_FILTER_STARTSWITH_BAR=bar \
-e VALHEIM_LOG_FILTER_STARTSWITH_SOMETHING_ELSE="some other filter"
-e VALHEIM_LOG_FILTER_CONTAINS_Connected="Got character ZDOID from"
-e ON_VALHEIM_LOG_FILTER_CONTAINS_Connected="cat >> /tmp/character_login"

Discord log filter event hook example

Sends a Discord message whenever a player spawns

-e DISCORD_WEBHOOK="https://discord.com/api/webhooks/8171522530..." \
-e VALHEIM_LOG_FILTER_CONTAINS_Spawned="Got character ZDOID from" \
-e ON_VALHEIM_LOG_FILTER_CONTAINS_Spawned='{ read l; l=${l//*ZDOID from /}; l=${l// :*/}; msg="Player $l spawned into the world"; curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$msg\"}" "$DISCORD_WEBHOOK"; }'

See Notify on Discord below for proper quoting in env and compose files.

If you are running ValheimPlus and this filter triggers twice, check this ValheimPlus issue. The cause is a misconfigured BepInEx.cfg that causes all log lines to be duplicated.

Event hooks

The following environment variables can be populated to run commands whenever specific events happen.

NameDefaultPurpose
PRE_SUPERVISOR_HOOKCommand to be executed before supervisord is run. Startup is blocked until this command returns.
PRE_BOOTSTRAP_HOOKCommand to be executed before bootstrapping is done. Startup is blocked until this command returns.
POST_BOOTSTRAP_HOOKCommand to be executed after bootstrapping is done and before the server or any services are started. Can be used to install additional packages or perform additional system setup. Startup is blocked until this command returns.
PRE_BACKUP_HOOKCommand to be executed before a backup is created. The string @BACKUP_FILE@ will be replaced by the full path of the future backup zip file. Backups are blocked until this command returns.
POST_BACKUP_HOOKCommand to be executed after a backup is created. The string @BACKUP_FILE@ will be replaced by the full path of the backup zip file. Backups are blocked until this command returns. See Copy backups to another location for details.
PRE_UPDATE_CHECK_HOOKCommand to be executed before an update check is performed. Current update is blocked until this command returns.
POST_UPDATE_CHECK_HOOKCommand to be executed after an update check was performed. Future updates are blocked until this command returns.
PRE_START_HOOKCommand to be executed before the first server start is performed by the valheim-updater. Current start is blocked until this command returns.
POST_START_HOOKCommand to be executed after the first server start was performed by the valheim-updater. Future restarts and update checks are blocked until this command returns.
PRE_RESTART_HOOKCommand to be executed before a server restart is performed by the valheim-updater. Current restart is blocked until this command returns.
PRE_SERVER_LISTENING_HOOKCommand to be executed after the server runs, but before it's able to accept connections. The loop that checks connection status will be blocked until this command returns.
POST_SERVER_LISTENING_HOOKCommand to be executed once the server is available for players to connect! The hook only fires after status is updated to running.
POST_RESTART_HOOKCommand to be executed after a server restart was performed by the valheim-updater. Future restarts and update checks are blocked until this command returns.
PRE_SERVER_RUN_HOOKCommand to be executed before the server is started. Server startup is blocked until this command returns.
POST_SERVER_RUN_HOOKCommand to be executed after the server has finished running. Server shutdown is blocked until this command returns or a shutdown timeout is triggered after 29 seconds.
PRE_SERVER_SHUTDOWN_HOOKCommand to be executed before the server is shut down. Server shutdown is blocked until this command returns. If PRE_SERVER_SHUTDOWN_HOOK holds the shutdown process for more than 90 seconds, the entire process will be hard-killed by supervisord.
POST_SERVER_SHUTDOWN_HOOKCommand to be executed after the server has finished shutting down.
PRE_BEPINEX_CONFIG_HOOKCommand to be executed before writing BepInEx.cfg.
POST_BEPINEX_CONFIG_HOOKCommand to be executed after writing BepInEx.cfg. Can be used to write your own mod config using env2cfg.
Event hook examples

Install extra packages

-e POST_BOOTSTRAP_HOOK="apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install awscli"

Copy backups to another location

After a backup ZIP has been created the command specified by $POST_BACKUP_HOOK will be executed if set to a non-zero string. Within that command the string @BACKUP_FILE@ will be replaced by the full path to the just created ZIP file.

-v $HOME/.ssh/id_rsa:/root/.ssh/id_rsa \
-v $HOME/.ssh/known_hosts:/root/.ssh/known_hosts \
-e POST_BACKUP_HOOK='timeout 300 scp @BACKUP_FILE@ myself@example.com:~/backups/$(basename @BACKUP_FILE@)'

Notify on Discord

Because proper string quoting on the shell vs. inside a docker-compose.yaml vs. an env_file can be challenging, here are examples for each use case.

Using the commandline

Delay restarts by 1 minute and notify on Discord

-e DISCORD_WEBHOOK="https://discord.com/api/webhooks/8171522530..." \
-e DISCORD_MESSAGE="Restarting Valheim server in one minute!" \
-e PRE_RESTART_HOOK='curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$DISCORD_MESSAGE\"}" "$DISCORD_WEBHOOK" && sleep 60'
Inside docker-compose.yaml

Notify on Discord with server's name in the message

    environment:
      - DISCORD_WEBHOOK=https://discord.com/api/webhooks/8171522530...
      - DISCORD_MESSAGE=Starting Valheim server $$SERVER_NAME
      - 'PRE_BOOTSTRAP_HOOK=curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$$(eval echo $$DISCORD_MESSAGE)\"}" "$$DISCORD_WEBHOOK"'
Inside an env_file
DISCORD_WEBHOOK=https://discord.com/api/webhooks/8171522530...
DISCORD_MESSAGE=Starting Valheim server
PRE_BOOTSTRAP_HOOK=curl -sfSL -X POST -H "Content-Type: application/json" -d "{\"username\":\"Valheim\",\"content\":\"$DISCORD_MESSAGE\"}" "$DISCORD_WEBHOOK"

Notify on Matrix, inside an env_file

Create an account for your bot, log in and join the room you want to post to. The room ID is noted in the room's settings.

MATRIX_BOT_SERVER=https://matrix...
MATRIX_BOT_ROOM_ID=!...
MATRIX_BOT_ACCESS_TOKEN=...
PRE_RESTART_HOOK=curl -sfSL -X PUT -d "{\"msgtype\":\"m.notice\",\"body\":\"Valheim is being updated\"}" "$MATRIX_BOT_SERVER/_matrix/client/r0/rooms/$MATRIX_BOT_ROOM_ID/send/m.room.message/$(date +%s-%N)?access_token=$MATRIX_BOT_ACCESS_TOKEN"

Note the $(date +%s-%N) is used for the required unique txnId.

Mod config from Environment Variables

Mod config can be specified in environment variables using the syntax <prefix>_<section>_<variable>=<value>.

Predefined prefix list

PrefixModFile
VPCFGValheimPlus/config/valheimplus/valheim_plus.cfg
BEPINEXCFGBepInEx/config/valheimplus/BepInEx.cfg or /config/bepinex/BepInEx.cfg depending on whether VALHEIM_PLUS=true or BEPINEX=true

Translation table
Some characters that are allowed as section names in the config files are not allowed as environment variable names. They can be encoded using the following translation table.

Variable name stringReplacement
_DOT_.
_HYPHEN_-
_UNDERSCORE__
_PLUS_+

Example:

-e VALHEIM_PLUS=true \
-e VPCFG_Server_enabled=true \
-e VPCFG_Server_enforceMod=false \
-e VPCFG_Server_dataRate=500 \
-e BEPINEXCFG_Logging_DOT_Console_Enabled=true

turns into /config/valheimplus/valheim_plus.cfg

[Server]
enabled=true
enforceMod=false
dataRate=500

and /config/valheimplus/BepInEx.cfg

[Logging.Console]
Enabled=true

All existing configuration in those files is retained and a backup of the old config is created as e.g. /config/valheimplus/valheim_plus.cfg.old before writing the new config file.

You could generate your own custom plugin config from environment variables using the POST_BEPINEX_CONFIG_HOOK event hook and env2cfg.

System requirements

On our system while idle with no players connected Valheim server consumes around 2.8 GB RSS. All the while using around 30% of one CPU Core on a 2.40 GHz Intel Xeon E5-2620 v3. Valheim server is making use of many threads with two of them seemingly doing the bulk of the work each responsible for around 8-10% of the 30% of idle load.

The picture changes when players connect. The first player increased overall load to 42%, the second player to 53%. In the thread view we see that a thread that was previously consuming 10% is now hovering around 38%. Meaning while Valheim server creates 50 threads on our system it looks like there is a single thread doing the bulk of all work (~70%) with no way for the Kernel to distribute the load to many cores.

Therefor our minimum requirements would be a dual core system with 4 GB of RAM and our recommended system would be a high clocked 4 core server with 8 GB of RAM. A few very high clocked cores will be more beneficial than having many cores. I.e. two 5 GHz cores will yield better performance than six 2 GHz cores. This holds especially true the more players are connected to the system.

Deployment

Deploying with Docker and systemd

Create a config file /etc/sysconfig/valheim-server

SERVER_NAME=My Server
SERVER_PORT=2456
WORLD_NAME=Dedicated
SERVER_PASS=secret
SERVER_PUBLIC=true

Then enable the Docker container on system boot

$ sudo mkdir -p /etc/valheim /opt/valheim
$ sudo curl -o /etc/systemd/system/valheim.service https://raw.githubusercontent.com/lloesche/valheim-server-docker/main/valheim.service
$ sudo systemctl daemon-reload
$ sudo systemctl enable valheim.service
$ sudo systemctl start valheim.service

Deploying with docker-compose

Copy'paste the following into your shell

mkdir -p $HOME/valheim-server/config $HOME/valheim-server/data
cd $HOME/valheim-server/
cat > $HOME/valheim-server/valheim.env << EOF
SERVER_NAME=My Server
WORLD_NAME=Dedicated
SERVER_PASS=secret
SERVER_PUBLIC=true
EOF
curl -o $HOME/valheim-server/docker-compose.yaml https://raw.githubusercontent.com/lloesche/valheim-server-docker/main/docker-compose.yaml
docker-compose up

Deploying to Kubernetes

Kubernetes manifests using this container image, along with a helm chart, are available from the following repository: https://github.com/Addyvan/valheim-k8s

The chart is also available directly using:

helm repo add valheim-k8s https://addyvan.github.io/valheim-k8s/
helm repo update
helm install valheim-server valheim-k8s/valheim-k8s # see repo for full config

Deploying to AWS ECS

CDK Project for spinning up a Valheim game server on AWS Using ECS Fargate and Amazon EFS is available here: https://github.com/rileydakota/valheim-ecs-fargate-cdk

Deploying to Nomad

$ sudo mkdir -p /var/lib/valheim/{config,data}
$ sudo curl -o /var/lib/valheim/valheim.nomad https://raw.githubusercontent.com/lloesche/valheim-server-docker/main/valheim.nomad
$ sudo nomad job run /var/lib/valheim/valheim.nomad

Updates

By default the container will check for Valheim server updates every 15 minutes if no players are currently connected to the server. If an update is found it is downloaded and the server restarted. This update schedule can be changed using the UPDATE_CRON environment variable.

Backups

The container will on startup and periodically create a backup of the worlds/ directory.

The default is once per hour but can be changed using the BACKUPS_CRON environment variable.

Default backup directory is /config/backups/ within the container. A different directory can be set using the BACKUPS_DIRECTORY environment variable. It makes sense to have this directory be a volume mount from the host. Warning: do not make the backup directory a subfolder of /config/worlds/. Otherwise each backup will backup all previous backups.

By default 3 days worth of backups will be kept. A different number can be configured using BACKUPS_MAX_AGE. The value is in days.

It is possible to configure a maximum number of to-be-kept backup files with BACKUPS_MAX_COUNT. When going over this limit, the oldest file(s) will be deleted. The default is 0 which means no limit. Note that BACKUPS_MAX_AGE will always be respected: if backups get too old, they will be deleted even if BACKUPS_MAX_COUNT was not yet reached (or is 0).

Beware that backups are performed whil

Docker Pull Command

docker pull lloesche/valheim-server