odin-vsm 1.0.0

Manage your Valheim dedicated server with confidence โ€” a fast, single-binary Rust CLI for Docker, mods, backups, and world sync.
Documentation

ODIN โ€” Valheim Server Manager

Odin is a modern Rust-powered CLI built for managing Dockerized Valheim dedicated servers with reliability, performance, and simplicity in mind. Designed around type-safe configuration, asynchronous mod downloads via the Thunderstore API, and structured error handling, it delivers a seamless server management experience through a single, dependency-free binary. From server lifecycle automation and BepInEx mod management to cross-platform world synchronization between Windows and Linux using rclone and Tailscale, Odin handles the infrastructure complexity so you can stay focused on the game.

Features

  • ๐Ÿš€ Server Lifecycle โ€” start, stop, restart, update, and manage containers with a single command
  • ๐Ÿงฉ Mod Management โ€” classify, download, and install mods from Thunderstore with automatic dependency resolution
  • ๐ŸŒ World Sync โ€” seamless cross-platform world synchronization between Windows and Linux via SSH/rclone
  • ๐Ÿ’พ Automated Backups โ€” scheduled world snapshots with restore capabilities and manual backup support
  • ๐Ÿฉบ Health Diagnostics โ€” comprehensive system, Docker, and configuration validation before first use
  • ๐Ÿ“Š Monitoring & Logs โ€” real-time server status, log streaming, and interactive shell access
  • ๐Ÿ› ๏ธ DLL Patching โ€” apply and verify assembly patches with toggle control via environment variables
  • โฐ Scheduled Tasks โ€” cron-based automation for updates, restarts, and backups
  • ๐Ÿ”Œ BepInEx & Valheim+ โ€” full mod loader support with mutually exclusive configuration
  • ๐ŸŽฎ Crossplay Support โ€” Xbox and Game Pass crossplay enablement
  • ๐Ÿงช Type-Safe Config โ€” compile-time validated configuration with sensible defaults
  • ๐Ÿ“ฆ Single Binary โ€” zero runtime dependencies beyond Docker and standard Unix tools

Table of contents

  1. Why Rust?
  2. Prerequisites
  3. Installation
  4. Configuration
  5. CLI commands
  6. Mod management
  7. World sync
  8. Build & test
  9. Project structure
  10. Best practices
  11. Troubleshooting

โšก Why Rust?

Odin leverages Rust's unique strengths to deliver a production-grade server manager that's both reliable and performant:

  • Type safety โ€” compile-time guarantees eliminate entire classes of runtime errors. Odin's configuration system, API responses, and error handling are all type-checked at build time, preventing silent failures in production.

  • Fearless concurrency โ€” async/await with Tokio enables safe, efficient parallel operations. Odin downloads mods concurrently from Thunderstore, monitors server health, and syncs worlds across networks without race conditions or deadlocks.

  • Memory safety without garbage collection โ€” Rust's ownership model guarantees memory safety while keeping Odin lightweight and predictable. No GC pauses means reliable server management even under sustained load.

  • Performance โ€” zero-cost abstractions and minimal overhead. Odin runs as a single, lean binary that starts instantly and uses negligible CPU/memory, ideal for always-on server orchestration.

  • Single binary โ€” no runtime dependencies beyond Docker and standard Unix tools. Deploy Odin anywhere: bare metal, containers, or embedded systems. No Python, Node, or JVM required.

  • Rich ecosystem โ€” Tokio (async runtime), Reqwest (HTTP client), Serde (serialization), Clap (CLI parsing). Odin uses battle-tested libraries that handle complexity so you don't have to.

  • Excellent error handling โ€” Result types and the ? operator make error propagation explicit and ergonomic. Server management demands reliability; Odin's error handling is unambiguous and recoverable.

  • Cross-platform compilation โ€” build once, run on Linux, macOS, or Windows. Odin's world sync bridges Windows and Linux seamlessly, powered by Rust's portable standard library.


๐Ÿ“‹ Prerequisites

System

Requirement Minimum Notes
Linux kernel 4.11+ overlay2 support for Docker
CPU cores 2 (4 recommended) Valheim idle โ‰ˆ 1โ€“2 cores
RAM 4 GB (8 GB recommended) Valheim idle โ‰ˆ 2.8 GB
Free disk 10 GB+ ~1 GB Docker image + world saves

Required binaries

Binary Purpose
docker + compose v2 Container runtime
7z Mod extraction, world backups
zip Project snapshots
rclone World sync (optional)
tailscale VPN for sync (optional)

Rust toolchain (build from source only)

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

๐Ÿš€ Installation

Option A โ€” cargo install (recommended)

cargo install odin
mkdir -p /srv/valheim && cd /srv/valheim
odin init        # interactive wizard โ€” fetches all config files from GitHub
odin health      # verify environment before first start

Option B โ€” Download binary from GitHub Releases

Download the pre-built binary for your platform from the Releases page, then:

chmod +x odin-linux-x86_64
mv odin-linux-x86_64 /srv/valheim/odin
cd /srv/valheim
./odin init      # interactive wizard โ€” fetches all config files from GitHub
./odin health

Option C โ€” Build from source

git clone https://github.com/RajaRakoto/odin-vsm.git && cd odin-vsm
cargo build --release
cp target/release/odin /srv/valheim/odin
cd /srv/valheim
./odin init

What odin init sets up

/srv/valheim/
โ”œโ”€โ”€ docker-compose.yaml   โ† fetched from GitHub (latest)
โ”œโ”€โ”€ odin                  โ† binary
โ”œโ”€โ”€ valheim.env           โ† generated from valheim.env.example + your answers
โ”œโ”€โ”€ scripts/              โ† fetched from GitHub (apply-patch.sh + any future scripts)
โ”œโ”€โ”€ config/               โ† created automatically on first start
โ”‚   โ”œโ”€โ”€ backups/
โ”‚   โ”œโ”€โ”€ worlds_local/
โ”‚   โ””โ”€โ”€ bepinex/plugins/
โ”œโ”€โ”€ data/                 โ† created automatically (steamcmd + Valheim binaries)
โ””โ”€โ”€ mods_list.txt         โ† optional

odin looks for valheim.env next to the binary, falling back to the working directory.


โš™๏ธ Configuration (valheim.env) โ€” Environment variables

Copy valheim.env.example to valheim.env and edit it.

Core server settings

Variable Default Description
SERVER_NAME My Server Name in the Steam server browser
WORLD_NAME Dedicated World save file name
SERVER_PASS (empty) Must be โ‰ฅ 5 characters
SERVER_PUBLIC false List publicly on Steam
TZ Etc/UTC Timezone for cron schedules

โฐ Automatic scheduling

Variable Description
UPDATE_CRON Auto-pull latest Docker image
RESTART_CRON Auto-restart container
BACKUPS_CRON Auto-backup world saves
0 4 * * *      โ†’ Daily at 04:00
0 */6 * * *    โ†’ Every 6 hours
*/30 * * * *   โ†’ Every 30 minutes

Features

Variable Default Description
CROSSPLAY false Xbox/Game Pass crossplay
SUPERVISOR_HTTP false Supervisor web UI on port 9001
BEPINEX false BepInEx mod loader
VALHEIM_PLUS false Valheim+ (mutually exclusive with BepInEx)
APPLY_DLL_PATCH false Enable/disable DLL patching โ€” the hook always runs, the script exits early when false
PRE_SERVER_RUN_HOOK /scripts/apply-patch.sh Fixed hook โ€” do not change; controls patch execution via APPLY_DLL_PATCH
PUID / PGID 1000 UID/GID owning ./data and ./config

Windows sync (optional)

Variable Description
WIN_HOST Windows IP or hostname (Tailscale IP recommended)
WIN_USER Windows account name
WIN_SSH_USER SSH login on Windows
WIN_SSH_PORT SSH port (default: 22)
WIN_SSH_KEY Absolute path to SSH private key

๐Ÿ’ป CLI commands

Run ./odin with no arguments to see the full command guide.

๐Ÿš€ Setup

odin init            # interactive wizard: fetch config from GitHub + generate valheim.env

Prompts for SERVER_NAME, WORLD_NAME, SERVER_PASS, TZ (auto-detected), and optional Windows sync config. Fetches docker-compose.yaml, valheim.env.example, and the full scripts/ directory from GitHub at runtime so you always get the latest defaults.

๐Ÿฉบ Diagnostic

odin health          # system, Docker, config, volumes, ports โ€” run before first use

Server lifecycle

odin start           # docker compose up -d
odin stop            # graceful stop (waits up to 2 min for world save)
odin restart         # docker compose restart
odin down            # remove container (volumes preserved)
odin update          # pull latest image and restart

Monitoring

odin status          # full server status (passwords hidden)
odin status-password # same, with passwords revealed
odin logs [N]        # stream logs (default: 50 lines)
odin shell           # interactive shell inside the container

๐Ÿ’พ Backup & restore

odin backup          # manual backup via Supervisor
odin clear-backups   # delete all backups in config/backups/ (interactive)
odin restore-worlds  # interactively restore a world backup
odin snapshot        # archive project to ~/valheim-server.bak.zip

Mod management

odin filter-mods     # classify mods via Thunderstore API โ†’ mods_list.txt
odin download-mods   # download mods to mods_cache/ (no extraction)
odin install-mods    # download + install mods to config/bepinex/plugins/
odin clear-mods      # stop server, backup worlds, remove mods (interactive)

World sync

odin sync-worlds --help-guide   # setup guide
odin sync-worlds                # โš  destructive โ€” overwrites server worlds

๐Ÿฉน DLL Patch

odin apply-patch     # apply APPLY_DLL_PATCH change from valheim.env (recreates container)
odin verify-patch    # verify the patched DLL is active -- shows MD5 + file sizes

APPLY_DLL_PATCH is the only toggle โ€” set it to true or false in valheim.env.

Important Docker behaviour: odin restart reuses the existing container environment. A change to APPLY_DLL_PATCH in valheim.env is only picked up after the container is recreated (down + start). Run odin apply-patch to do this automatically: it reads the current value from valheim.env, confirms with you, then runs docker compose down + docker compose up -d.

Once the fresh container starts, PRE_SERVER_RUN_HOOK=/scripts/apply-patch.sh runs before every Valheim startup and applies or skips the patch based on APPLY_DLL_PATCH.

Requires ./patches/assembly_valheim.dll and ./scripts/apply-patch.sh (both mounted read-only in docker-compose.yaml).

Fixes

odin fix permission  # chown 1000:1000 + chmod 755 on ./data and ./config

๐Ÿ“ฆ Mod management workflow

odin uses mods_list.txt as the source of truth.

# Format: Author-ModName-Version  (version optional โ€” always fetches latest)
Azumatt-AzuAutoStore-1.2.3
ValheimModding-Jotunn-2.20.0
# SomeAuthor-ClientOnlyMod-1.0.0*    โ† * โ†’ skip entirely
# SomeAuthor-ForceBothMod-1.0.0**    โ† ** โ†’ force classify as "both"
odin filter-mods   # Step 1 โ€” classify
odin install-mods  # Step 2 โ€” install server-side + both mods
odin start         # Step 3 โ€” launch

Updating mods:

odin clear-mods    # stop, backup, remove
# edit mods_list.txt
odin install-mods && odin start

๐ŸŒ World sync (Windows โ†’ Linux)

Copies Valheim save files from Windows to the Linux server via rclone SFTP.

# On Windows: enable OpenSSH Server (Settings โ†’ Apps โ†’ Optional features)

# On Linux:
ssh-keygen -t ed25519 -f ~/.ssh/valheim_sync
ssh-copy-id -i ~/.ssh/valheim_sync.pub user@windows-ip

# In valheim.env:
WIN_HOST=100.x.x.x   # Tailscale IP recommended
WIN_SSH_KEY=/home/youruser/.ssh/valheim_sync
odin sync-worlds --help-guide   # read first
odin backup && odin sync-worlds # โš  destructive
odin start

Sync aborts if Valheim.exe is running on Windows or players are connected.


๐Ÿ”จ Build & test

cargo build --release                                          # standard build
cargo build --release --target x86_64-unknown-linux-musl      # static binary

cargo test                  # all tests
cargo clippy -- -D warnings # lints
cargo fmt --check           # formatting

๐Ÿ“ Project structure

src/
โ”œโ”€โ”€ main.rs           โ€” entry point (env load, banner, dispatch)
โ”œโ”€โ”€ cli.rs            โ€” clap CLI (all commands)
โ”œโ”€โ”€ config.rs         โ€” AppConfig (valheim.env โ†’ typed struct)
โ”œโ”€โ”€ error.rs          โ€” Error enum + Result alias
โ”œโ”€โ”€ api/
โ”‚   โ””โ”€โ”€ thunderstore.rs  โ€” Thunderstore REST API client
โ”œโ”€โ”€ commands/
โ”‚   โ”œโ”€โ”€ backups.rs    โ€” clear-backups
โ”‚   โ”œโ”€โ”€ docker.rs     โ€” start / stop / restart / down / logs / update / shell
โ”‚   โ”œโ”€โ”€ fix.rs        โ€” fix permission
โ”‚   โ”œโ”€โ”€ health.rs     โ€” health diagnostic
โ”‚   โ”œโ”€โ”€ init.rs       โ€” init wizard (fetch config + scripts from GitHub)
โ”‚   โ”œโ”€โ”€ mods.rs       โ€” filter / download / install / clear mods
โ”‚   โ”œโ”€โ”€ patch.rs      โ€” apply-patch / verify-patch
โ”‚   โ”œโ”€โ”€ status.rs     โ€” status / status-password
โ”‚   โ””โ”€โ”€ worlds.rs     โ€” restore-worlds / sync-worlds
โ””โ”€โ”€ utils/
    โ”œโ”€โ”€ banner.rs     โ€” print_banner() / print_help()
    โ”œโ”€โ”€ display.rs    โ€” info / ok / warn / err / confirm
    โ”œโ”€โ”€ fs.rs         โ€” sudo_run / sudo_rm_rf / sudo_mkdir_p
    โ””โ”€โ”€ net.rs        โ€” internal_ips() / external_ip()

๐ŸŽฎ Best practices

  • Run odin health before first use โ€” validates the full environment.
  • Set SERVER_PASS to โ‰ฅ 5 characters or the server won't start.
  • Match PUID/PGID to the user owning ./data and ./config (id -u && id -g).
  • Enable BACKUPS_CRON for automatic world snapshots; always backup before odin update.
  • Start with a small mod set (5โ€“10) to validate the pipeline before scaling up.
  • Use a Tailscale IP for WIN_HOST โ€” encrypted, works across networks.

๐Ÿ”ง Troubleshooting

Docker daemon not running

sudo systemctl start docker && sudo systemctl enable docker

Permission errors on startup

odin fix permission

SERVER_PASS too short โ€” must be โ‰ฅ 5 characters; update valheim.env then odin start.

ZFS โ€” steamcmd fails with "250 MB required"

zfs set quota=500G <pool/dataset>

odin health reports jq missing โ€” not required; all API calls are handled natively.

Mods not loading after install-mods โ€” confirm BEPINEX=true, check config/bepinex/plugins/ is non-empty, then odin restart.

sync-worlds SSH refused โ€” confirm OpenSSH Server is running on Windows and Tailscale is active on both machines.

DLL patch not taking effect after changing APPLY_DLL_PATCH โ€” odin restart does not re-read valheim.env. Run odin apply-patch to recreate the container with the new value. Use odin verify-patch to confirm.


License

MIT โ€” see LICENSE.