197 lines
8.4 KiB
Markdown
197 lines
8.4 KiB
Markdown
# Valheim Server StackScript
|
|
|
|
A Linode StackScript that turns a freshly created Debian 13 Linode into a
|
|
Valheim dedicated server. Running it as a StackScript means everything happens
|
|
as root during deployment, so no SSH session (and therefore no SSH key) is
|
|
needed to configure the machine.
|
|
|
|
It does four things, in order:
|
|
|
|
1. Installs Docker from Debian's own `docker.io` package.
|
|
2. Mounts the pre-existing Valheim volume (this mirrors `voldirections.txt`;
|
|
`mkfs.ext4` is **not** run because the filesystem already exists).
|
|
3. Creates the persistent `config/` and `data/` directories on the volume.
|
|
4. Starts the Valheim server container with those directories bind-mounted, so
|
|
worlds, configuration and backups survive destroying and recreating the
|
|
Linode.
|
|
|
|
## Instructions
|
|
|
|
1. Log in to the Linode Cloud Manager and open **StackScripts**.
|
|
2. Click **Create StackScript**.
|
|
3. Set **Label** to something like `valheim-server` and choose **Debian 13**
|
|
(or "Any") as the **Target Images**.
|
|
4. Paste the code block below into the **Script** editor.
|
|
5. Edit the server settings at the top of the script (name, world, password)
|
|
before saving.
|
|
6. Leave the **UDF** (User Defined Fields) section empty — the values are
|
|
hard-coded, so nothing needs to be entered at deploy time.
|
|
7. Click **Save**. The numeric ID of the StackScript appears in the URL and in
|
|
the StackScript list; copy it.
|
|
8. Put that ID in `create-linode.sh` as the `STACKSCRIPT_ID` value near the top
|
|
of the file.
|
|
|
|
After that, `create-linode.sh c <label>` passes the StackScript on the create
|
|
call and the Linode comes up as a running Valheim server with no further steps.
|
|
|
|
## Where things live on the volume
|
|
|
|
Everything the container needs to keep is under `/mnt/ValVol202609/valheim`:
|
|
|
|
| Host path | Container path | Contents |
|
|
| --- | --- | --- |
|
|
| `/mnt/ValVol202609/valheim/config` | `/config` | World saves, `adminlist.txt`, `bannedlist.txt`, `permittedlist.txt`, `backups/` |
|
|
| `/mnt/ValVol202609/valheim/data` | `/opt/valheim` | The ~1 GB downloaded server plus SteamCMD's depot cache |
|
|
|
|
Mounting `/opt/valheim` as well means a newly created Linode does not have to
|
|
re-download the server from Steam, which saves several minutes per instance.
|
|
The depot cache lives inside that same directory so it stays in sync with the
|
|
installation it belongs to.
|
|
|
|
Worlds are stored in `config/worlds_local/<WORLD_NAME>/` (older releases used a
|
|
single `<WORLD_NAME>.db`/`.fwl` pair directly in `config/worlds/`). To bring an
|
|
existing world along, copy it into the `worlds_local` directory on the volume
|
|
and set `WORLD_NAME` to match.
|
|
|
|
## Notes
|
|
|
|
- **Ports.** The container publishes `2456-2457/udp` (game and query). Linodes
|
|
have no inbound firewall by default, so nothing else is required. If you add
|
|
a Cloud Firewall, allow those two UDP ports.
|
|
- **Sizing.** The upstream project recommends a high-clocked 4 core / 8 GB
|
|
machine, which is exactly `g6-standard-4` — the type `create-linode.sh` uses.
|
|
- **First start.** The container downloads ~1 GB from Steam on first boot and
|
|
takes several minutes to become joinable.
|
|
- **Admin.** Once the server has started, `config/adminlist.txt` exists on the
|
|
volume. Put your SteamID64 in it to get in-game admin commands.
|
|
- **Backups.** The container writes hourly world backups to `config/backups/`
|
|
on the volume and prunes them after 3 days.
|
|
- **Updates.** The container checks for Valheim updates every 15 minutes and
|
|
restarts the server when one is found, but only while nobody is connected.
|
|
- **Re-running.** The script is safe to run again: the mount and the `fstab`
|
|
entry are only created if missing, and any previous container is removed
|
|
before the new one is started.
|
|
|
|
## Sensitive data
|
|
|
|
- There is a secrets file in gitignore
|
|
- The IDs for all linode API calls are linked to my account, but non-sensitive.
|
|
- The passwords are placeholders, edited when setting up StackScripts
|
|
- Repo can be made public, as no git snapshot had sensitive data.
|
|
|
|
## StackScript
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# Linode StackScript: turn a fresh Debian 13 Linode into a Valheim dedicated
|
|
# server whose worlds, configuration and backups live on the pre-existing
|
|
# Valheim volume. Runs as root during deployment; no SSH access required.
|
|
|
|
set -euo pipefail
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Server settings - edit these before saving the StackScript.
|
|
# ---------------------------------------------------------------------------
|
|
SERVER_NAME="Valheim Server"
|
|
WORLD_NAME="Dedicated"
|
|
# Must be at least 5 characters, otherwise valheim_server.x86_64 refuses to
|
|
# start. Anything saved here is readable by anyone with access to the
|
|
# StackScript, so change it.
|
|
SERVER_PASS="changeme123"
|
|
# true lists the server in the in-game community browser.
|
|
SERVER_PUBLIC="true"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Volume and container settings.
|
|
# ---------------------------------------------------------------------------
|
|
VOLUME_NAME="ValVol202609"
|
|
VOLUME_DEVICE="/dev/disk/by-id/scsi-0Linode_Volume_${VOLUME_NAME}"
|
|
VOLUME_MOUNT="/mnt/${VOLUME_NAME}"
|
|
|
|
# Everything the container has to keep across Linode rebuilds lives here.
|
|
VALHEIM_DIR="${VOLUME_MOUNT}/valheim"
|
|
CONFIG_DIR="${VALHEIM_DIR}/config"
|
|
DATA_DIR="${VALHEIM_DIR}/data"
|
|
|
|
CONTAINER_NAME="valheim-server"
|
|
# The upstream project moved to ghcr.io/community-valheim-tools/valheim-server.
|
|
# The Docker Hub image below is still published and can be swapped for it.
|
|
IMAGE="lloesche/valheim-server"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Install Docker.
|
|
# Debian's docker.io package is enough for a single `docker run` and avoids
|
|
# adding a third-party apt repository. ca-certificates is listed explicitly
|
|
# because --no-install-recommends would otherwise skip it and image pulls
|
|
# would fail TLS verification.
|
|
# ---------------------------------------------------------------------------
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get update
|
|
apt-get install -y --no-install-recommends docker.io ca-certificates docker-cli
|
|
systemctl enable --now docker
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Mount the Valheim volume.
|
|
# The volume is attached over the Linode API once the instance is running, so
|
|
# wait for the device to appear. 60 attempts x 5s = 5 minute ceiling.
|
|
# ---------------------------------------------------------------------------
|
|
for _ in $(seq 1 60); do
|
|
if [ -e "$VOLUME_DEVICE" ]; then
|
|
break
|
|
fi
|
|
echo "Waiting for ${VOLUME_DEVICE} to appear..."
|
|
sleep 5
|
|
done
|
|
|
|
if [ ! -e "$VOLUME_DEVICE" ]; then
|
|
echo "ERROR: ${VOLUME_DEVICE} never appeared. Aborting so that the world is not written to the instance's ephemeral disk." >&2
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$VOLUME_MOUNT"
|
|
if ! mountpoint -q "$VOLUME_MOUNT"; then
|
|
mount "$VOLUME_DEVICE" "$VOLUME_MOUNT"
|
|
fi
|
|
|
|
# Mount on every boot, without duplicating the line on re-runs. nofail keeps a
|
|
# missing volume from blocking boot; if the volume is not there the mountpoint
|
|
# silently becomes a directory on the instance's own disk, which is why the
|
|
# check above aborts rather than continuing.
|
|
if ! grep -q "$VOLUME_DEVICE" /etc/fstab; then
|
|
echo "${VOLUME_DEVICE} ${VOLUME_MOUNT} ext4 defaults,noatime,nofail 0 2" >> /etc/fstab
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Create the persistent directories on the volume.
|
|
# ---------------------------------------------------------------------------
|
|
mkdir -p "$CONFIG_DIR" "$DATA_DIR"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. Start the Valheim server.
|
|
# --restart unless-stopped brings the server back after a reboot.
|
|
# --stop-timeout 120 gives the server time to save the world on shutdown.
|
|
# --cap-add=sys_nice lets the Steam library raise its own thread priority.
|
|
# ---------------------------------------------------------------------------
|
|
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
|
|
|
docker run -d \
|
|
--name "$CONTAINER_NAME" \
|
|
--cap-add=sys_nice \
|
|
--stop-timeout 120 \
|
|
--restart unless-stopped \
|
|
-p 2456-2457:2456-2457/udp \
|
|
-v "${CONFIG_DIR}:/config" \
|
|
-v "${DATA_DIR}:/opt/valheim" \
|
|
-e SERVER_NAME="$SERVER_NAME" \
|
|
-e WORLD_NAME="$WORLD_NAME" \
|
|
-e SERVER_PASS="$SERVER_PASS" \
|
|
-e SERVER_PUBLIC="$SERVER_PUBLIC" \
|
|
-e SERVER_ARGS="-modifier resources more" \
|
|
"$IMAGE"
|
|
|
|
echo "Valheim server started."
|
|
echo "The first start downloads ~1GB from Steam and takes several minutes."
|
|
echo "Follow progress with: docker logs -f $CONTAINER_NAME"
|
|
```
|
|
|