Modding Valheim on a Mac is mostly the same as on Windows, with three differences that will each cost you an afternoon if nobody tells you: Gatekeeper quarantines the files, Steam’s Play button bypasses BepInEx entirely, and on Apple Silicon the shipped launch script loads no plugins while reporting nothing.

Install BepInEx Link to heading

Download BepInEx_macos_universal_5.4.23.5.zip from the BepInEx releases page and unzip it directly into the game directory, next to Valheim.app:

~/Library/Application\ Support/Steam/steamapps/common/Valheim

You should end up with:

Valheim/
├── Valheim.app
├── BepInEx/
├── libdoorstop.dylib
├── run_bepinex.sh
├── changelog.txt
└── .doorstop_version

macOS quarantines anything it downloaded. Clear that first, or the dylib is blocked without telling you why:

cd ~/Library/Application\ Support/Steam/steamapps/common/Valheim
xattr -dr com.apple.quarantine libdoorstop.dylib BepInEx
chmod +x run_bepinex.sh

Then run the game once through the script:

./run_bepinex.sh ./Valheim.app

On an Intel Mac you now have BepInEx/LogOutput.log and you are done with this section. On Apple Silicon you have BepInEx/config/BepInEx.cfg and no log at all. Keep reading.

The Apple Silicon line Link to heading

BepInEx 5 cannot patch code when the game runs as native arm64. It loads no plugins, writes no log, and does not report the failure. The workaround is to run the game translated instead.

Open run_bepinex.sh and find this block near the bottom — line 324 in 5.4.23.5:

if [ -n "${is_apple_silicon}" ]; then
    export ARCHPREFERENCE="arm64,x86_64"

Reverse the two:

    export ARCHPREFERENCE="x86_64,arm64"

Launch again and the log appears within ten seconds, with your plugins in it.

That preference order is the only lever. Wrapping the script in arch -x86_64 does nothing, because the script re-exports ARCHPREFERENCE and re-execs through arch itself.

The upstream value is deliberate, and its comment explains the intent — stop an x86_64 parent process from silently downgrading a universal game binary. That reasoning is sound. It just does not account for BepInEx being unable to patch code once the game really is running as arm64. Why that happens, and the fix for it, are in running Valheim mods without Rosetta.

Surviving Steam updates Link to heading

A Steam update restores the stock run_bepinex.sh, and your mods go quiet again without saying anything. Rather than remember the edit, wrap it. I keep this as launch-modded.sh in the game directory:

#!/bin/sh
V="$HOME/Library/Application Support/Steam/steamapps/common/Valheim"
cd "$V" || exit 1

# A Steam update restores the stock run_bepinex.sh, and the wrong architecture
# then loads no plugins while saying nothing at all. Re-apply the one edit.
WANT='ARCHPREFERENCE="x86_64,arm64"'
if ! grep -q "$WANT" run_bepinex.sh; then
    sed -i '' "s/export ARCHPREFERENCE=.*/export $WANT/" run_bepinex.sh
fi

if [ $# -gt 0 ]; then
    exec ./run_bepinex.sh "$@"
fi
exec ./run_bepinex.sh ./Valheim.app
chmod +x launch-modded.sh

Configure Steam Link to heading

Steam’s Play button starts Valheim.app directly and loads no plugins at all. Point it at the wrapper instead.

Right-click Valheim in your library, then Properties → General → Launch Options, and enter the script path followed by %command%:

"/Users/you/Library/Application Support/Steam/steamapps/common/Valheim/launch-modded.sh" %command%

The quotes are required, because the path contains a space. Steam passes the inner executable path as the argument, and run_bepinex.sh accepts either that or the .app, so launching from Steam and launching from a terminal both work.

Going through Steam rather than running the script by hand is what keeps the overlay, playtime tracking and friend invites working.

Installing mods Link to heading

BepInEx loads any DLL it finds in BepInEx/plugins. There is no macOS mod manager worth the trouble, so install by hand:

  1. Download the mod from Thunderstore.
  2. Unzip it. The DLL is usually at the top level or under a plugins/ directory.
  3. Copy the DLL into BepInEx/plugins/.
  4. Clear the quarantine flag: xattr -dr com.apple.quarantine BepInEx/plugins.

Some mods ship a folder of assets alongside the DLL. Copy the whole folder, keeping its structure.

Mods write their config on first launch, not at install time. Start the game once, quit, then edit the .cfg file that appears in BepInEx/config/. A two-mod install ends up looking like this:

BepInEx/
├── plugins/
│   ├── ExampleMod.dll
│   └── AnotherMod.dll
├── config/
│   ├── BepInEx.cfg
│   ├── com.example.examplemod.cfg
│   └── com.example.anothermod.cfg
└── LogOutput.log

The config filenames come from each mod’s plugin GUID, not its filename, so they rarely match the DLL you copied in.

If you play on a server, mods that change game behaviour generally have to be installed on both sides at matching versions. Client-only mods — UI, map, quality of life — do not.

Checking it worked Link to heading

head -5 "$HOME/Library/Application Support/Steam/steamapps/common/Valheim/BepInEx/LogOutput.log"
[Message:   BepInEx] BepInEx 5.4.23.5 - Valheim (2026-09-18 7:24:32 AM)
[Info   :   BepInEx] Detected Unity version: v6000.0.75f1
[Message:   BepInEx] Preloader started
[Info   :   BepInEx] Patching [UnityEngine.CoreModule] with [BepInEx.Chainloader]
[Message:   BepInEx] Preloader finished

Then look for a Loading [<your mod>] line further down.

If that file does not exist at all, the architecture edit did not take. Check that Steam is launching the wrapper and not the app, and look in Valheim.app/Contents/MacOS/preloader_*.log — on Apple Silicon that is where the real error goes.

Feedback Link to heading

This is the setup I actually run, on an M4 Pro. If something here does not match what you see — particularly on an Intel Mac, which I no longer have to test against — tell me and I will correct it.