Posted on
Artificial Intelligence

Artificial Intelligence Valheim Server Administration

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Artificial Intelligence Valheim Server Administration with Bash

Level up your Valheim server ops with lightweight, local AI that reads your logs, flags incidents, helps moderate chat, and even suggests performance tweaks—all from Bash.

If you’ve ever been paged at 2 a.m. because the world stopped saving, players are disconnecting in waves, or you can’t tell if it’s a mod mismatch or a crash loop, this guide is for you. We’ll show how to graft a small local LLM into your Linux toolbelt to automate the boring parts, keep humans in the loop, and make better decisions, faster.

What you’ll get:

  • A minimal, containerized Valheim server baseline

  • Local AI runtime (no cloud bill required)

  • 3–5 actionable Bash scripts that:

    • Summarize logs and detect incidents
    • Flag toxic chat and alert moderators
    • Triage performance with actionable recommendations
    • Back up and sanity-check your world data
  • Cron- or systemd-timer-ready commands

Note: This is a Linux/Bash-first workflow. You can adapt to your existing server build—Docker, Podman, or bare-metal—by adjusting paths and commands.


Why AI makes sense for Valheim server ops

  • Logs are noisy. AI can condense a 500-line dump into a 3-line incident report with a recommended action.

  • Player experience is fragile. Fast detection of disconnect storms, save failures, and mod/version mismatches minimizes downtime.

  • Moderation is hard. A classifier can flag likely harassment/toxicity and ping humans—without auto-banning.

  • It’s private and cheap. Small, local models (e.g., Llama 3.x 3B) are enough for classification and summarization and run on commodity CPUs.


Prerequisites and install commands

We’ll need: containers (Docker), CLI utilities (jq, curl, tmux, bc, unzip), and a local AI runtime (Ollama).

Install packages (choose your distro):

  • Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y docker.io docker-compose-plugin jq curl tmux bc unzip
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
  • Fedora/RHEL/CentOS Stream (dnf):
sudo dnf -y install moby-engine docker-compose-plugin jq curl tmux bc unzip
sudo systemctl enable --now docker
sudo usermod -aG docker $USER
  • openSUSE Leap/Tumbleweed (zypper):
sudo zypper refresh
sudo zypper install -y docker docker-compose jq curl tmux bc unzip
sudo systemctl enable --now docker
sudo usermod -aG docker $USER

Re-log to apply the docker group (or run newgrp docker).

Optional (Podman users):

  • Replace docker with podman and docker-compose with podman-compose where suitable.

Quick-start: Containerized Valheim server

Use your preferred image. The example below shows a common pattern; check your image’s README for the precise environment variables and volume paths (they vary by image).

Example docker-compose.yml (skeleton):

mkdir -p ~/valheim/{data,backups}
cd ~/valheim

cat > docker-compose.yml <<'YAML'
services:
  valheim:
    image: lloesche/valheim-server
    container_name: valheim
    restart: unless-stopped
    environment:
      - SERVER_NAME=My Valheim Server
      - WORLD_NAME=Dedicated
      - SERVER_PASS=SuperSecret123
      - SERVER_PUBLIC=1
      - TZ=Etc/UTC
      - UPDATE_ON_BOOT=1
      - BACKUPS=1
    volumes:
      - ./data:/config
      - ./backups:/backups
    ports:
      - "2456-2458:2456-2458/udp"
YAML

docker compose up -d

Notes:

  • Ports 2456–2458/udp must be open on your firewall/router.

  • Many images store worlds under /config/worlds or similar. Confirm your image docs and adjust paths in the scripts below accordingly.


Install a local AI runtime (Ollama)

Ollama runs small open models locally. We’ll use it for JSON-style classification/summarization.

curl -fsSL https://ollama.com/install.sh | sh
sudo systemctl enable --now ollama || true
# Pull a small, fast model (CPU-friendly)
ollama pull llama3.2

You can substitute any model you prefer (e.g., qwen2.5:3b, phi3, mistral). Adjust the MODEL variable in scripts below.


Core AI-assisted admin recipes (Bash)

Each script is drop-in, no extra Python needed. Put them in ~/valheim/bin and chmod +x.

1) AI log summarizer and incident detector

Continuously summarizes recent server logs and flags likely incidents (crash loops, save failures, mass disconnects, mod mismatch). Sends a concise JSON and can ping a webhook (Discord/Slack/etc.).

mkdir -p ~/valheim/bin ~/valheim/logs
cat > ~/valheim/bin/ai_logs_watch.sh <<'BASH'
#!/usr/bin/env bash
set -euo pipefail

CONTAINER="${CONTAINER:-valheim}"
MODEL="${MODEL:-llama3.2}"
INTERVAL="${INTERVAL:-60}"   # seconds
SINCE="${SINCE:-10m}"       # docker logs window
TAIL="${TAIL:-400}"         # max lines to analyze
WEBHOOK_URL="${WEBHOOK_URL:-}" # optional: https://discord.com/api/webhooks/...

ollama_ok() { command -v ollama >/dev/null 2>&1; }
docker_ok() { command -v docker  >/dev/null 2>&1; }

ask_ai() {
  local prompt="$1"
  printf "%s\n" "$prompt" | ollama run "$MODEL"
}

while true; do
  if ! docker_ok || ! ollama_ok; then
    echo "[ERR] missing docker or ollama; sleeping..."
    sleep "$INTERVAL"; continue
  fi

  LOGS="$(docker logs --since "$SINCE" --tail "$TAIL" "$CONTAINER" 2>/dev/null || true)"
  [ -z "$LOGS" ] && { sleep "$INTERVAL"; continue; }

  PROMPT=$(
    cat <<'EOF'
You are an SRE for a Valheim dedicated server. Analyze the following log excerpt.
Identify if there is an incident. Consider crash loops, repeated disconnects, save failures,
mod/version mismatch, corrupted world, or network issues.
Output one-line JSON with keys:
{"incident": true|false, "severity": "info|low|medium|high|critical", "short_title": "...",
"reason": "...", "recommended_action": "...", "signals": ["...","..."]}

Logs:
EOF
  )
  RESPONSE="$(ask_ai "$PROMPT
$LOGS
")" || RESPONSE='{"incident":false,"severity":"info","short_title":"AI error","reason":"LLM error","recommended_action":"Retry","signals":[]}'
  echo "$RESPONSE" | tee -a ~/valheim/logs/incidents.jsonl

  if [ -n "$WEBHOOK_URL" ]; then
    # Try to parse severity. If not valid JSON, still forward text.
    SEV=$(echo "$RESPONSE" | jq -r .severity 2>/dev/null || echo "info")
    if [[ "$SEV" =~ (high|critical) ]]; then
      PAYLOAD=$(jq -cn --arg content "[Valheim AI] $RESPONSE" '{content:$content}')
      curl -fsSL -H "Content-Type: application/json" -d "$PAYLOAD" "$WEBHOOK_URL" >/dev/null || true
    fi
  fi

  sleep "$INTERVAL"
done
BASH
chmod +x ~/valheim/bin/ai_logs_watch.sh

Run it (tmux recommended so it keeps running):

tmux new -s valheim-ai-logs '~/valheim/bin/ai_logs_watch.sh'

Tip: Set WEBHOOK_URL to your Discord/Slack incoming webhook to receive high/critical incidents.


2) AI-assisted chat toxicity flagger (notify, don’t auto-ban)

Valheim logs/chat format differs by image/mod. Adjust the grep to your server’s chat lines. This script classifies messages and sends alerts for human review.

cat > ~/valheim/bin/ai_chat_watch.sh <<'BASH'
#!/usr/bin/env bash
set -euo pipefail

CONTAINER="${CONTAINER:-valheim}"
MODEL="${MODEL:-llama3.2}"
WEBHOOK_URL="${WEBHOOK_URL:-}" # optional Discord/Slack webhook
SINCE="${SINCE:-2m}"
TAIL="${TAIL:-400}"

extract_chat() {
  # Adjust this filter to your image's chat format.
  # Common patterns include "Chat:" or "SAY" lines.
  docker logs --since "$SINCE" --tail "$TAIL" "$CONTAINER" 2>/dev/null \
    | grep -E "Chat:|SAY|chat" || true
}

classify() {
  local text="$1"
  local prompt='Classify the following game chat line for potential policy issues.
Output a single-line JSON: {"toxic":true|false,"category":"harassment|hate|spam|none","confidence":0-1,"explanation":"..."}
Text: '"$text"
  printf "%s\n" "$prompt" | ollama run "$MODEL"
}

main() {
  local lines
  lines="$(extract_chat)"
  [ -z "$lines" ] && exit 0

  while IFS= read -r line; do
    [ -z "$line" ] && continue
    res="$(classify "$line")"
    echo "$res" | tee -a ~/valheim/logs/chat_flags.jsonl

    toxic=$(echo "$res" | jq -r .toxic 2>/dev/null || echo "false")
    if [ "$toxic" = "true" ] && [ -n "$WEBHOOK_URL" ]; then
      payload=$(jq -cn --arg content "[Valheim Chat Flag] $line -> $res" '{content:$content}')
      curl -fsSL -H "Content-Type: application/json" -d "$payload" "$WEBHOOK_URL" >/dev/null || true
    fi
  done <<< "$lines"
}

main
BASH
chmod +x ~/valheim/bin/ai_chat_watch.sh

# Run every minute via cron (example):
# * * * * * CONTAINER=valheim WEBHOOK_URL=https://... /home/$USER/valheim/bin/ai_chat_watch.sh

Keep humans in the loop. This is for alerts and triage, not punishment.


3) AI performance triage (metrics -> suggestions)

Collect lightweight metrics and ask the AI for focused, safe suggestions. It won’t touch the server—just recommends.

cat > ~/valheim/bin/ai_perf_triage.sh <<'BASH'
#!/usr/bin/env bash
set -euo pipefail

CONTAINER="${CONTAINER:-valheim}"
MODEL="${MODEL:-llama3.2}"

read_metrics() {
  echo "timestamp=$(date -Is)"
  echo "loadavg=$(cut -d' ' -f1-3 /proc/loadavg)"
  echo "mem_MiB_total=$(free -m | awk '/Mem:/ {print $2}')"
  echo "mem_MiB_used=$(free -m | awk '/Mem:/ {print $3}')"
  echo "swap_MiB_used=$(free -m | awk '/Swap:/ {print $3}')"
  echo "cpu_count=$(getconf _NPROCESSORS_ONLN)"
  echo "uptime_sec=$(cut -d' ' -f1 /proc/uptime)"
  docker stats --no-stream --format '{{json .}}' "$CONTAINER" 2>/dev/null | jq -r '
    . as $s | "docker_name=\($s.Name)\n" +
    "docker_cpu=\($s.CPUPerc)\n" +
    "docker_mem=\($s.MemUsage)\n" +
    "docker_net=\($s.NetIO)\n" +
    "docker_block=\($s.BlockIO)"
  ' || true
}

prompt() {
  cat <<'EOF'
You are optimizing a Valheim dedicated server. Given these host and container metrics,
give 3 practical, low-risk recommendations. Consider CPU saturation, memory pressure,
container memory limits, save frequency, mod load, world size, and network IO.

Constraints:

- Do NOT suggest destructive actions.

- Prefer config hints (e.g., reduce draw distance via configs/mods, increase save interval safely, suggest world cleanup).

- Output as one-line JSON with keys:
{"summary":"...", "recommendations": ["...", "...", "..."], "urgency":"low|medium|high"}
EOF
}

METRICS="$(read_metrics)"
RES="$(printf "%s\n\nMetrics:\n%s\n" "$(prompt)" "$METRICS" | ollama run "$MODEL")"
echo "$RES" | tee -a ~/valheim/logs/perf_triage.jsonl
BASH
chmod +x ~/valheim/bin/ai_perf_triage.sh

# Example on-demand run:
~/valheim/bin/ai_perf_triage.sh

You’ll get concise JSON output you can paste into a ticket or change plan.


4) Backups with AI sanity-check notes

Back up world data and let AI produce a short “backup health” note (not a substitute for restore testing!).

Adjust DATA_DIR to match your volume mapping (often ./data or ./data/worlds).

cat > ~/valheim/bin/backup_and_note.sh <<'BASH'
#!/usr/bin/env bash
set -euo pipefail

DATA_DIR="${DATA_DIR:-$HOME/valheim/data}"
BACKUP_DIR="${BACKUP_DIR:-$HOME/valheim/backups}"
MODEL="${MODEL:-llama3.2}"

ts="$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
tarball="$BACKUP_DIR/world-$ts.tar.gz"

# Create archive and manifest
tar -czf "$tarball" -C "$DATA_DIR" . 2>/dev/null
manifest="$tarball.sha256"
( cd "$DATA_DIR" && find . -type f -print0 | sort -z | xargs -0 sha256sum ) > "$manifest"

size_bytes=$(stat -c%s "$tarball")
files_count=$(wc -l < "$manifest")

# Ask AI for a short, human-readable note
note=$(cat <<EOF | ollama run "$MODEL"
Given a Valheim world backup with:

- archive_size_bytes: $size_bytes

- files_count: $files_count

- known risks: corrupted world if server crashed during save, stale mods, not-tested restores
Write a one-paragraph "backup health note" and 2 bullet reminders.
Reply in plain text, keep it under 80 words.
EOF
)

echo "$note" > "$tarball.note.txt"
echo "Backup: $tarball"
echo "Manifest: $manifest"
echo "Note:"
echo "$note"
BASH
chmod +x ~/valheim/bin/backup_and_note.sh

# Cron example: daily at 04:30
# 30 4 * * * DATA_DIR=/home/$USER/valheim/data BACKUP_DIR=/home/$USER/valheim/backups /home/$USER/valheim/bin/backup_and_note.sh

Remember: the only real backup is one you have restored. Schedule a monthly test restore.


Putting it on rails (cron/systemd)

  • Logs: run the watcher in tmux or a systemd service.

  • Chat: run via cron every minute or as a service tailing logs.

  • Performance triage: cron hourly is fine.

  • Backups: nightly cron, offsite sync with rsync/rclone.

Example systemd unit for the log watcher:

sudo tee /etc/systemd/system/valheim-ai-logs.service >/dev/null <<'UNIT'
[Unit]
Description=Valheim AI Log Watcher
After=network-online.target docker.service
Wants=docker.service

[Service]
User=YOURUSER
Environment=CONTAINER=valheim
Environment=MODEL=llama3.2
WorkingDirectory=/home/YOURUSER/valheim
ExecStart=/home/YOURUSER/valheim/bin/ai_logs_watch.sh
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
UNIT

sudo systemctl daemon-reload
sudo systemctl enable --now valheim-ai-logs

Replace YOURUSER and paths accordingly.


Real-world examples you’ll see

  • Incident digest: “critical, world save failing repeatedly; recommend check disk space, validate permissions on /config/worlds, increase save interval temporarily, restart during off-peak.”

  • Chat alert: “toxic: harassment (0.87). Explanation: direct insults targeted at playername.”

  • Perf triage: “medium urgency. Recommendations: raise container memory limit or reduce mod count; schedule weekly restart; verify save interval and network jitter.”


Caveats and good practices

  • Privacy: running locally avoids sending player data off-host. Still, treat logs as sensitive.

  • Non-determinism: LLMs can be creative. That’s why we use JSON prompts and human review.

  • Model choice matters: smaller models are fast but simpler. If accuracy matters, try a slightly larger model.

  • Don’t auto-punish: Notify, then moderate. Keep trust with your community.


Conclusion and next steps (CTA)

You now have an AI sidekick for your Valheim server—summarizing logs, flagging risky chat, triaging performance, and documenting backups—all from Bash on your Linux box.

Next steps:

  • Wire the log and chat scripts to your Discord/Slack webhook.

  • Tune the grep filters for your image/mods.

  • Add a monthly restore test job to a staging server.

  • Experiment with different models in Ollama (MODEL=qwen2.5:3b or mistral).

Have a clever prompt or improvement? Package it as a script and share it with your community. Happy admin’ing, and may your mead halls never crash mid-raid!