How to host a Valheim server on Apple Silicon
Vikings. Building stuff. Killing bosses. All on your own server.


Valheim is a fun (and addictive) game about Vikings, building stuff, and killing bosses. Playing it alone is decent, but it’s most fun when playing with your friends. The best way to do this is to host your own server. There are many options for this, but using your own hardware is the best and least expensive option. I’ve found that in both performance-to-watt and price-to-performance, the M1 Mac mini makes for an incredible server. In fact, I’m using one to host this site as well as my own Valheim server. Trying to host it on Apple Silicon took a bit of research, however, since as of writing Valheim (and Valheim server) only natively supports x86 processors. Fortunately, Apple Silicon is extremely efficient at emulating x86, and you can host a Valheim server with no hiccups even on the base model 8GB M1 Mac mini. Let’s walk through how to do it!
Table of Contents
- Necessary software
- Configure your Valheim VM and container environment
- Deploy your Valheim server container
- Access your server from the Internet
- Conclusion
- Troubleshooting
Necessary software
- Homebrew
- lima-vm/lima (
brew install lima
) - socket_vmnet (
brew install socket_vmnet
after installing lima) - lloesche/valheim-server
Configure your Valheim VM and container environment
Before starting your VM instance, you’ll need to configure your VM and container manifests. First, create a project folder:
mkdir -p $HOME/valheim-configs
cd $HOME/valheim-configs
Set up Lima VM
Lima VM is the best option I’ve found for using “slow” QEMU emulation, which fully emulates x86 architecture instead of partial “fast” emulation that you may find using something like Docker Desktop, Parallels, or multipass. Lima is also much more lightweight than VMs such as UTM or VirtualBox.
To install Lima, first you must have Homebrew. Follow the installation instructions on the Homebrew site to install brew
, then run brew install lima
. After installation finishes, install the included socket_vmnet service, which will automatically forward ports from the VM through the host, by running brew install socket_vmnet
. Now you’re ready to configure the VM manifest file.
Lima config
Create the following .yaml manifest, titled valheim-server.yaml
:
# full QEMU emulation
arch: 'x86_64'
# must be minimum 50GB
disk: 50GiB
# amount needed depends on player count
# generally half of what Intel needs
# recommend at least 4GiB
memory: 6GiB
# recommend at least 3 cpus
cpus: 4
# bridged creates separate IP on your LAN with the hostname "lima-<vm-name>"
networks:
- lima: bridged
# To run `docker` on the host (assumes docker-cli is installed):
# $ export DOCKER_HOST=$(limactl list docker --format 'unix://{{.Dir}}/sock/docker.sock')
# $ docker ...
# This example requires Lima v0.8.0 or later
images:
# Try to use release-yyyyMMdd image if available. Note that release-yyyyMMdd will be removed after several months.
- location: "https://cloud-images.ubuntu.com/releases/22.04/release-20221201/ubuntu-22.04-server-cloudimg-amd64.img"
arch: "x86_64"
digest: "sha256:8a814737df484d9e2f4cb2c04c91629aea2fced6799fc36f77376f0da91dba65"
- location: "https://cloud-images.ubuntu.com/releases/22.04/release-20221201/ubuntu-22.04-server-cloudimg-arm64.img"
arch: "aarch64"
digest: "sha256:8a0477adcbdadefd58ae5c0625b53bbe618aedfe69983b824da8d02be0a8c961"
# Fallback to the latest release image.
# Hint: run `limactl prune` to invalidate the cache
- location: "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-amd64.img"
arch: "x86_64"
- location: "https://cloud-images.ubuntu.com/releases/22.04/release/ubuntu-22.04-server-cloudimg-arm64.img"
arch: "aarch64"
mounts:
- location: "~"
- location: "/tmp/lima"
writable: true
# containerd is managed by Docker, not by Lima, so the values are set to false here.
containerd:
system: false
user: false
provision:
- mode: system
# This script defines the host.docker.internal hostname when hostResolver is disabled.
# It is also needed for lima 0.8.2 and earlier, which does not support hostResolver.hosts.
# Names defined in /etc/hosts inside the VM are not resolved inside containers when
# using the hostResolver; use hostResolver.hosts instead (requires lima 0.8.3 or later).
script: |
#!/bin/sh
sed -i 's/host.lima.internal.*/host.lima.internal host.docker.internal/' /etc/hosts
- mode: system
script: |
#!/bin/bash
set -eux -o pipefail
command -v docker >/dev/null 2>&1 && exit 0
export DEBIAN_FRONTEND=noninteractive
curl -fsSL https://get.docker.com | sh
# NOTE: you may remove the lines below, if you prefer to use rootful docker, not rootless
systemctl disable --now docker
apt-get install -y uidmap dbus-user-session
- mode: user
script: |
#!/bin/bash
set -eux -o pipefail
systemctl --user start dbus
dockerd-rootless-setuptool.sh install
docker context use rootless
probes:
- script: |
#!/bin/bash
set -eux -o pipefail
if ! timeout 30s bash -c "until command -v docker >/dev/null 2>&1; do sleep 3; done"; then
echo >&2 "docker is not installed yet"
exit 1
fi
if ! timeout 30s bash -c "until pgrep rootlesskit; do sleep 3; done"; then
echo >&2 "rootlesskit (used by rootless docker) is not running"
exit 1
fi
hint: See "/var/log/cloud-init-output.log". in the guest
hostResolver:
# hostResolver.hosts requires lima 0.8.3 or later. Names defined here will also
# resolve inside containers, and not just inside the VM itself.
hosts:
host.docker.internal: host.lima.internal
portForwards:
- guestSocket: "/run/user/{{.UID}}/docker.sock"
hostSocket: "{{.Dir}}/sock/docker.sock"
message: |
To run `docker` on the host (assumes docker-cli is installed), run the following commands:
------
docker context create lima-{{.Name}} --docker "host=unix://{{.Dir}}/sock/docker.sock"
docker context use lima-{{.Name}}
docker run hello-world
------
Container config
Now create a docker compose manifest titled docker-compose.yaml
:
version: "3"
services:
valheim:
image: ghcr.io/lloesche/valheim-server
cap_add:
- sys_nice
volumes:
- $HOME/valheim-server/config:/config
- $HOME/valheim-server/data:/opt/valheim
- /Users/$USER/.ssh/id_ecdsa_valheim:/root/.ssh/id_ecdsa:ro
- /Users/$USER/.ssh/known_hosts:/root/.ssh/known_hosts
ports:
- "2456-2457:2456-2457/udp"
env_file:
- ./valheim.env
restart: always
stop_grace_period: 2m
And to accompany, create a valheim.env
file to reference:
SERVER_NAME=ServerName
WORLD_NAME=WorldName
SERVER_PASS=securepassword
SERVER_PUBLIC=true
TZ='US/Central'
POST_BACKUP_HOOK='timeout 300 cp @BACKUP_FILE@ $HOME/backups/$(basename @BACKUP_FILE@)'
Be sure to change the TZ var to your time zone. Things like auto-restart and auto-update rely on knowing the right time to perform certain actions so they’ll be done early in the morning rather than mid-day.
The POST_BACKUP_HOOK
var can be used to copy your world backups to a different location after a local backup is made. I’d recommend copying them to either your host machine via scp
or a remote file server to have for extra redundancy.
If you want to copy your backup back to your host machine, I’ve already added a volume reference to /Users/$USER/.ssh/id_ecdsa_valheim
in the docker compose file. To create this file, run:
ssh-keygen -t ecdsa -f /Users/$USER/.ssh/id_ecdsa_valheim -N ''
Now you have an SSH key for securely communicating between your VM and host machine without needing a password.
Deploy your Valheim server container
Initialise your Lima VM with:
# you can add this export to your .zshrc file as well
# otherwise it reverts to 'default' on shell restart
export LIMA_INSTANCE='valheim-server' &&
limactl start --name=$LIMA_INSTANCE $HOME/valheim-server/valheim-server.yaml
Once your VM has finished initialising, enter the VM via:
# alias for `limactl shell $LIMA_INSTANCE`
lima
Now you’ll need to copy over your docker-compose and env files.
mkdir -p ~/valheim-server &&
cd ~/valheim-server &&
cp /Users/$USER/valheim-configs/docker-compose.yaml . &&
cp /Users/$USER/valheim-configs/valheim.env . &&
# start docker container
docker compose up -d --remove-orphans
Starting the container will create two folders in your ~/valheim-server/
directory: config/
and data/
. These are where all of your Valheim server and world info is stored, so don’t delete them!
Now you can view the server logs with:
docker logs -f valheim-server-valheim-1
Now you should see the server logs live, and will see the line supervisord: valheim-server DEBUG - [125] - Waiting for server to listen on UDP port 2456
throughout the logs for the first 2 minutes after starting the server. Once you see a message stating supervisord: valheim-server DEBUG - [125] - Server is now listening on UDP port 2456
, your server will be ready to join. Before you’re able to join from the Internet, however, there’s one final step of forwarding your ports.
Access your server from the Internet
Forward ports
Now that your Lima instance is up and running, a new IP will show up on your LAN with a host name like lima-valheim-server
. Forward the necessary UDP ports from that IP (default 2456-2457/udp
) on your router. Since each router has a different way of doing this, I can’t post specific instructions here, so refer to the documentation for your router.
Check server status and logs
The easiest way to see if your ports are properly forwarded and the server is working is to use https://geekstrom.de/valheim/check/, but this does require that your server is public. Once you see a status like below, your server is ready to go!

Join your server
Now you and your friends are ready to start up your game and play!
After starting your game, select “Start Game,” create or select your character, then go to the “Join Game” tab and select “Add Server.”

Put in your sever name, press Return, then select “Connect” under the “Add server” button.

It should take a few seconds until an “Enter Password” prompt appears. This will be the password you set in valheim.env
.

After you enter your password, your world will load and you’re ready to explore!

Conclusion
Now you can go have fun with your friends in your own Valheim server! Cheers to many buildings built and many bosses killed in the journey to Valhalla.
Apple Silicon offers an incredibly powerful and efficient architecture for running a server. The level of sustained performance I’ve been able to get out of just an 8GB M1 Mac mini is impressive, especially at such a small energy footprint. I’m looking forward for what’s next, especially for the use of scaling up my homelab.
Troubleshooting
Server says it’s listening on port but it’s still inaccessible
- Be sure your ports are open on your router, and that you’re forwarding ports from the guest IP (lima-.lan) and not your host IP.
- Stop the server, run
brew reinstall socket_vmnet
, and start the server again. Sometimes thesocket_vmnet
service seems to not be properly initialised. - Relatedly, make sure to not run
socket_vmnet
as a service on your host machine. The brew install gives directions to do this, but don’t do it. Lima starts its ownsocket_vmnet
service when starting the VM. - Make sure you have at least 50GB free space for your VM. Not sure why this is a requirement, but anything under that seems to prevent the server from working properly.
Container error mentioning incorrect architecture
- Be sure you’ve included
arch: x86_64
in your lima config file. Otherwise it defaults to “fast” QEMU emulation, where we want “slow” (full) emulation.
Anything else
Open an issue/PR for lloesche/valheim-server or lima-vm/lima respectively.