Skip to content

BepInEx

Ryan McAfee edited this page Sep 20, 2026 · 2 revisions

BepInEx

Updating changes nothing about the BepInEx you already have. Nothing is written and nothing is replaced until you answer the question below with a yes. If you say no, or if you close the question without answering it, BakaLoader never writes to your loader at all.

From 1.2.1 nothing is asked of Thunderstore either, until that yes. In 1.2.0 one request still went out whatever you had answered: inside a restart window BakaLoader asked Thunderstore which BepInEx pack was current before it read your answer. It named the package and nothing about you or your install, and nothing was downloaded, but it went out. In 1.2.1 the answer is read first, and without a yes the install is not even looked at, let alone the site asked. See Privacy and network.

That is worth saying first, because 1.2.0 was the release where BakaLoader learned to look after BepInEx, and most people reading this already have BepInEx installed.

BepInEx is the loader every Valheim mod runs under. Without it a plugins folder full of mods is a folder full of files the game never opens.

Until 1.2.0 BakaLoader said nothing about it at all. It scanned your mods, told you which ones had updates, and left the one thing underneath them to you. From 1.2.0 there is a row above the mod table that says whether BepInEx is installed, which version you have, whether anything is wrong with it, and whether BakaLoader is keeping it current.

The row

It sits above the table on the Mods screen, marked with a rune, named BepInEx, and it is the same row on every server. That is not a simplification: servers set up from one install share one BepInEx through links on disk, so there is only ever one answer to give. See What is shared below.

The row carries a version, a pill, a sentence and, in most of its states, a button. There are ten states, because a real install turns up in more shapes than "there" and "not there", and each of them needs a different answer from you.

Pill What it means The button
Missing Not installed, so nothing in this list would load. Install BepInEx
Looked after Kept up to date by BakaLoader. none, because the next restart window moves it
Yours to update BakaLoader put this pack in and is not looking after it. Update to the current pack
Outside Installed outside BakaLoader, so there is no telling whether a newer pack is out. Update to the current pack, which asks first
Outside (after a drift) Something else has written to this BepInEx since BakaLoader put it there, so BakaLoader stopped looking after it. Update to the current pack, which asks first
Another tool Another mod manager drives this BepInEx, so BakaLoader leaves it alone. Update to the current pack, which asks first and says what it would cost
Different loader The BepInEx here (6.0.0.0) is not the framework the pack BakaLoader installs carries, so it is left alone. Update to the current pack, which asks first
Unrecognised There is a BepInEx folder here that BakaLoader does not recognise, and nothing in it loads mods. Put the saved copy back when there is one, and Install BepInEx
Damaged The BepInEx folder here is missing the file that loads everything else, so no mod would load. Put the saved copy back when there is one, and Install BepInEx
Incomplete winhttp.dll is gone from this install, so BepInEx would not load. An antivirus taking that file is the usual cause. Put the missing files back

Looked after and Yours to update are the same install. Which of the two you see is your answer to the question below, read together with the Upkeep switch, and that is the whole of what the switch changes about this screen. Before you have answered, the switch reads off and the row says Yours to update, whatever the setting on disk happens to default to.

A write that did not finish (drawn from 1.2.1)

An install BakaLoader wrote can read as Outside, which looks wrong and is not. The commonest reason is a write that stopped part way: the files went in and the note recording them could not be finished, so nothing on disk vouches for them. BakaLoader tries to settle that every time it reads the install, and while it cannot, a line under the row says so:

A write here did not finish and the mark it left is still on disk. BakaLoader tries to settle it every time it reads this install, so something is still holding the note beside these files, and that is why an install it wrote can read as one it did not. Stop every server on this install, close anything reading that folder, then press Update.

The fact behind that line was being sent from the first version of the row and never drawn, which left the commonest way into Outside with nothing on screen to explain it.

The version reads differently in the two cases, and hovering it says which you are looking at. On an install BakaLoader wrote, it is the Thunderstore pack version, read from the note BakaLoader leaves beside the install. Anywhere else it is the loader's own file version, and the tooltip is honest about the gap: Nothing on disk records which pack it came in, so a pack bump cannot be seen from here.

Under the row, one line each, are the things that are true of it at the moment:

  • Checked again at every scheduled restart. on a looked after install.
  • You have said yes to BakaLoader looking after BepInEx, so this install is brought into its care at the next scheduled restart. on an install BakaLoader did not make, with an Open the setting press beside it so you can change your mind before that restart comes.
  • Version 5.4.2350 is ready and goes in once every server on this install has stopped. when an update is waiting on a running server.
  • BepInEx 5.4.2400 came out too recently for a restart to put it in unwatched. A restart after 23 September 04:00 will take it, and you can install it by hand before then. See the 72 hour wait.
  • Counted as loading this same BepInEx as 2 other servers. when more than one profile shares it.
  • BepInEx is being written right now.
  • A BepInEx pack was unpacked under the plugins folder, where it does nothing at all. which gets its own line, its own folder name and its own Remove the misplaced folder button.

The switch, and the one time question

The switch is in the Upkeep card on the Dashboard, directly under Update mods at scheduled restarts:

BepInEx kept up to date by BakaLoader

Installs BepInEx when it is missing, and at a scheduled restart replaces the core and the loader files with the current Thunderstore pack. A BepInEx that is already here is brought into its care the same way, with a copy of everything replaced kept beside the install; your mods, mod configs and BepInEx.cfg are left as they are, and so is your doorstop_config.ini unless the new pack needs a different one. An install another mod manager drives is never touched. Every server on this install shares one BepInEx, so this covers all of them.

The question is put twice, in two places, because a host who never presses Start would otherwise never see it. It is the same question and the same two answers both times.

The first time you start a server it comes up as a dialog. On the Dashboard it also stands on the condition bar until you answer it. Answering in either place answers it everywhere, and so does moving the Upkeep switch by hand.

What the question says depends on what is actually on your disk.

With nothing installed:

Let BakaLoader look after BepInEx?

BepInEx is the loader every mod runs under. BakaLoader can put it in when it is missing and move it to a newer pack during a scheduled restart.

With a BepInEx already there that BakaLoader did not put down:

BepInEx is already here. Let BakaLoader look after it?

This install already has BepInEx (core 5.4.23.5), put in by you or by another tool. Say yes and BakaLoader brings it into its care at the next scheduled restart: it replaces the BepInEx core and the loader files beside the server with the current pack from Thunderstore, and moves it to a newer pack whenever one comes out.

Your BepInEx.cfg, every mod, every mod config and the patchers folder are left exactly as they are. Your doorstop_config.ini is kept too, unless the new pack needs a different one, and then yours goes into the backup with everything else. A copy of everything BakaLoader replaces goes into BepInEx\.bakaloader-bepinex-backups first.

Say no and BakaLoader never writes to it.

With an install another mod manager drives:

BepInEx is already here. Let BakaLoader look after it?

Another mod manager drives the BepInEx beside this server: its own doorstop_config.ini points at that tool's profile folder. BakaLoader leaves this install alone whichever way you answer, because writing here would swap the whole mod set that tool looks after. Your answer covers any BepInEx you ask BakaLoader for later.

There is a fourth wording for a core that is not a BepInEx 5 at all, which says the same thing: that install is left alone either way.

The two answers are Yes, look after it and No, I will handle it. Yes is the one the dialog leads with. Closing the dialog with Escape, or clicking outside it, or dismissing the row on the Dashboard, is not an answer: nothing is written, the setting stays exactly as it was, and the question comes back. Until you answer, BakaLoader writes nothing to the loader on its own, and the Upkeep switch reads off however the preference behind it is stored.

Whichever you pick, it is not asked again, and the switch in Upkeep is where you change your mind.

What the switch does

With it on, and only once you have said so:

  • A server about to start with no BepInEx gets one put in first, in the same moment of the start where BakaLoader installs its own companion plugins.
  • A newer pack goes in during a scheduled restart, in the same window your mod updates already use.
  • Pasting a Thunderstore link with no BepInEx installed no longer refuses. BakaLoader fetches BepInEx first, shows you how far along it is, and then carries on and installs the mod you pasted. You do not paste the link again.
  • A BepInEx you already have, the Outside one, is brought into BakaLoader's care at the next scheduled restart. What that means in files is in What happens to an install that is already there below. The row says it is going to happen and puts the switch one press away, so you can stop it before that restart comes.
  • A file the note lists that has gone missing is named on the row, with a button that puts it back, and a start or a restart window puts it back on its own. That is the antivirus case further down.

Four kinds of install are never written to at a restart, whatever the switch says: one another mod manager drives, one whose core is not a BepInEx 5, one holding files BakaLoader does not recognise, and one somebody else has written to since BakaLoader put it there. Those all wait for you to press the button and answer the question that comes with it.

With it off:

  • Nothing is written unless you press a button.
  • Over an install BakaLoader did not put down, that button asks first and names what it would replace. See Every write over somebody else's install asks first.
  • The install flow asks first. It shows you the address it would fetch from, so you can change it, and only then continues.

What happens to an install that is already there

This is the part most people want in plain terms, so here it is as a list.

Replaced:

winhttp.dll                  beside valheim_server.exe
.doorstop_version            beside valheim_server.exe
changelog.txt                beside valheim_server.exe
BepInEx\core\                the whole folder, swapped for the pack's

Kept, exactly as you have it:

BepInEx\config\              every mod config, and your BepInEx.cfg
BepInEx\plugins\             every mod
BepInEx\patchers\            everything
doorstop_config.ini          your own edits included, unless the pack changes
                             the loader generation
everything else in the install

Your doorstop_config.ini is the one file a mod manager and a hand install are most likely to have edited, and it is yours. BakaLoader keeps it. The single exception is a pack that changes the loader generation, where the old file cannot drive the new loader: the pack's version goes in, yours goes into the backup with everything else that was replaced, and a line says so when the write is over. The question you answer before the write says the same, so you are told before as well as after.

Before anything moves, every file that is about to be replaced is opened for writing and closed again. If anything on the machine has one of them open, nothing is written at all and you get a sentence naming the file. That check is there because the older code found out the hard way: with a core file held the way a loaded assembly holds it, the writer removed eight files and then could not put them back.

And then the swap. The new core is unpacked into a folder beside the old one, the old core is moved into the backup folder, and the new one is moved into its place. Moving is what makes it safe: nothing is ever emptied and then refilled, so there is no moment where the install is half a loader.

Where the backup is, and how to put it back

Every write keeps a copy of everything it replaced:

<server folder>\BepInEx\.bakaloader-bepinex-backups\20260920-011500\core\...
<server folder>\BepInEx\.bakaloader-bepinex-backups\20260920-011500\root\...

The folder name is the date and time of the write. core holds the BepInEx core that was there before it; root holds the loose files beside the executable, your doorstop_config.ini among them if that write had to replace it. It sits under BepInEx, where no mod scan ever reads it.

The three most recent are kept, and beside them the one described next, which is kept for good. Three is enough to step back from an update that went wrong and past the one before it, and past that the pack it came from is gone from Thunderstore's current listing anyway.

One backup is kept for good, and it is the one taken on the write that brought your own BepInEx into BakaLoader's care. Every other backup holds a loader BakaLoader itself wrote, which you can always fetch again; that one holds yours, which you cannot. Keeping three drops the oldest first, and the oldest is exactly that one, so it is marked with a small .bakaloader-adoption.json file inside it and the tidying steps over it.

To put one back, use the Put the saved copy back button on the row. It appears whenever there is a saved copy and the install is broken or unrecognised, and it asks you first, naming the copy it would put back. When the BepInEx you had before BakaLoader is a different copy from the newest one, the same question offers it by name, with the core version it holds, under Put my own BepInEx back instead. Putting a copy back does not throw away what is there now: the current core goes into a fresh backup first, so you can go forwards again.

You can also do it by hand with every server on that install stopped: move BepInEx\core somewhere else, copy the core folder out of the backup into its place, and copy the contents of root back beside valheim_server.exe.

Another mod manager

A host coming from r2modman, Gale or Thunderstore Mod Manager has a doorstop_config.ini whose target_assembly points into that tool's profile folder rather than at the BepInEx beside the server. That is how those tools switch mod sets: the loader beside the game is theirs to aim.

BakaLoader reads that file now. When it points somewhere else, the row says Another tool, nothing is ever written there at a restart, and the question above tells you the install is left alone whichever way you answer. Writing over it would swap the entire mod set that tool looks after, and it would not know.

If you really do want BakaLoader to take it, the Update to the current pack button is still there. It asks first, and the question leads with the warning:

Another mod manager drives this install. Writing here swaps the whole mod set that tool looks after, and it will not know.

Nothing goes out until you press Write it.

A newer install

Pack versions and BepInEx's own file version are different numbers, so BakaLoader compares like with like: after it has the pack, it reads the file version of the BepInEx.dll inside it and the one on your disk.

If what you have is newer than what Thunderstore is offering, a restart writes nothing and the row says so. Pressing the button by hand does not move it either. What you get instead is:

Put an older BepInEx in?

The BepInEx here (5.4.23.5) is newer than the pack Thunderstore offers (5.4.22.0). Saying yes moves this install backwards, and a mod built for the newer one may stop loading.

That is the only way an install ever goes backwards, and it takes an explicit press.

A locked file

If a file that is about to be replaced is open, nothing is written:

Something has BepInEx.dll open, so nothing was written. Stop every server on this install, close anything reading that folder, and try again.

The usual causes are a server still running on that install, a second copy of BakaLoader, or a file browser sitting in the folder with a preview pane open. Antivirus software scanning the folder can do it too, for a second at a time.

Nothing is half written when this happens. The check runs before the first byte moves.

Antivirus taking winhttp.dll

winhttp.dll beside valheim_server.exe is the whole loading mechanism on Windows. There is no launch flag: the game picks that DLL up out of its own search order. A DLL appearing beside a game executable is exactly the shape of thing antivirus software looks for, and it is quarantined more often than anything else in this page.

An install whose loader files have gone missing since BakaLoader wrote them reads as Incomplete rather than as missing, and it names the file:

winhttp.dll is gone from this install, so BepInEx would not load. An antivirus taking that file is the usual cause.

The same sentence stands on the condition bar with a Put the missing files back button, so you see it from the Dashboard as well as from the Mods screen. With the switch on, the next start or restart window puts it back on its own, out of the pack the note names rather than out of whatever the site is offering today. Putting a file back is not an update, so the three day wait and the rules beside it do not hold it up.

An install BakaLoader did not write has no note for a file to be missing from, so there is nothing recording what should be there. The row still names winhttp.dll when the core is on disk and that file is not, because that is the one file the whole mechanism is, and the button still puts it back after the question about writing over somebody else's install. Nothing automatic happens there: the restart window brings that install into care the ordinary way, with the whole set replaced and a backup of what it replaced.

Check your antivirus quarantine before you do anything else, and add an exclusion for the server folder if it keeps happening. Putting the file back with the exclusion still missing just gives it something to take again.

When the window runs

The step that moves BepInEx runs inside the restart window, with the server down. From 1.2.0 it runs at every restart that has a window: scheduled, manual, empty server, crash recovery. It does not depend on mod auto update being on and it does not depend on a mod update being pending, which is where it used to sit and why a host with mod updates off never had the loader looked at.

It costs one version lookup. An unreachable Thunderstore writes nothing and the server still comes back.

The 72 hour wait

None of this applies to a REPAIR. Putting back a file an antivirus took is not a new loader going in: the pack that comes back is the one the note names, which is the one already running on this install, so the three day wait and the rules beside it have nothing to say about it and do not hold it up.

A pack that Thunderstore listed less than 72 hours ago does not go in at a restart. Somebody else finds the bad upload first. The row names the version and says when it becomes eligible:

BepInEx 5.4.2400 came out too recently for a restart to put it in unwatched. A restart after 23 September 04:00 will take it, and you can install it by hand before then.

The button is not held back by any of this. Neither are pre-releases, which a restart also never takes, nor a package Thunderstore has flagged as one to move away from, nor a version Thunderstore has taken down altogether. Those four all say so on the row, and the manual press ignores all four.

One more thing a restart will not do: unpack a download nobody could check. Thunderstore's live endpoint does not publish a size, so BakaLoader takes the size for that exact version out of the community index it already reads. With no size from either place a restart refuses, and a press goes ahead with a line in the log saying nothing was verified. Putting back a file that went missing is checked a different way, and a stricter one: BakaLoader fetches the version its own note names and holds every file in that download against the checksum it wrote down when it installed that version. One file that does not match and nothing is written.

Every write over somebody else's install asks first

Any press that would write over a BepInEx BakaLoader did not put down stops and asks:

Write BepInEx over the install that is here?

The BepInEx beside this server is core 5.4.23.5, and BakaLoader did not put it there. Saying yes replaces the core and the loader files with Thunderstore pack 5.4.2350.

Your BepInEx.cfg, every mod, every mod config and the patchers folder are left exactly as they are. Your doorstop_config.ini is kept too, unless the new pack needs a different one, and then yours goes into the backup with everything else. A copy of everything BakaLoader replaces goes into BepInEx\.bakaloader-bepinex-backups first.

If the install is one another tool drives, or a different framework, or a folder of files nothing recognises, the warning for that case rides above it. If you have already said yes to BakaLoader looking after BepInEx, the dialog says so as well and carries an Open the setting press, so the notice is reachable from the button you are actually pressing.

This is not just a manner. Those answers travel with the write, and BakaLoader refuses the write without them, so there is no way to reach that write except through the question.

What is shared between servers

One install, one BepInEx. A server profile with its own isolated install still shares BepInEx/core and BepInEx/patchers with the base through directory junctions, and the loose loader files beside the executable are hard links to the base's. Writing BepInEx anywhere therefore writes it everywhere on that install.

That is why the row is not per profile, why it names the profiles it covers, and why a write is refused while any of them is up.

Every button on the row that would write the loader is greyed while that is true, rather than failing when you press it, and hovering it says why:

Stop the servers that are up first. BakaLoader counts them as loading this same BepInEx.

A write asked for anyway comes back naming them: BakaLoader counts these servers as loading this same BepInEx, so nothing is written while one of them is up. Still up: Midgard, Test. While a write is actually running, the same buttons are greyed with BepInEx is being written right now. instead.

The unattended path defers rather than writing: a BepInEx update waiting row appears on the condition bar, naming the version and which servers it is waiting for, and the write happens at the next window when they are all down.

A server BakaLoader did not start counts too. Before a write it looks for a live valheim_server process running out of this install, whether or not BakaLoader is the thing that launched it, and refuses with the same sentence.

The reason is not caution for its own sake. Replacing BepInEx/core under a world that is up replaces the files a running game has open, on an install two other servers are also reading.

A profile whose install is on another drive holds real copies of the loose loader files rather than hard links to the base's. After every write that finished, those copies are refreshed from the base, so a profile never ends up running a new core under an old winhttp.dll. A profile with its own real BepInEx\core, rather than a link to the base's, is left entirely alone.

A world running with no mods

If a server is up, this profile has mods, and the loader under it is missing, broken or unrecognised, the condition bar says so plainly:

This world is running without its mods

BepInEx is not loading here, so all 14 mods on this server are doing nothing and the people on it are playing plain Valheim. Stop the server, put BepInEx back, and start it again.

That is a different row from BepInEx did not load below. This one is about files that are not there; that one is about files that are there and did not run.

When it says BepInEx did not load

A server that has been up a while with BepInEx installed, and no sign that the loader ran, raises a warning on the condition bar:

BepInEx did not load

This server has been up a while and BepInEx never wrote its log, so no mod is loaded. The wiki page says what to check.

The Read the wiki page button on that row opens this page. Here is the list.

Is the server path the right one? BepInEx loads out of the folder the executable sits in. A profile pointed at a second copy of valheim_server.exe somewhere else has a BepInEx beside the first copy and none beside the one it is running. The Currently line in the Directories section of Settings says which folder that server really uses.

Is winhttp.dll beside the executable? On Windows that file is the whole mechanism: there is no launch flag, the game picks the DLL up out of its own search order. Anti-virus software quarantines it more often than anything else in this list, because a DLL appearing beside a game executable is exactly the shape of the thing it is looking for. Check the quarantine before anything else.

Did something replace the install? Verifying the game files through Steam removes it, and so does a server update in some setups. Press Install BepInEx on the row, or let a looked after install put it back at the next start.

Is doorstop_config.ini still pointed at BepInEx? Another tool that manages the same install can rewrite it.

Is there a denikson-BepInExPack_Valheim folder under BepInEx/plugins? That is a pack unpacked in the wrong place, which loads nothing. The row says so and offers to remove it, and the real install is left alone.

If the loader did run, BepInEx/LogOutput.log will have been rewritten at the moment the server started. That file is what the check reads, and reading it yourself is the fastest way to confirm what the row is telling you.

When an install is refused

Each of these is a sentence you will actually see, and none of them leaves a half written loader behind.

That download is not a BepInEx pack, so nothing was written. the archive has no BepInEx.dll in it
The BepInEx download did not match what the site listed, so nothing was written. the bytes did not weigh what the listing published
That download is larger than BakaLoader will fetch for a loader, so it was stopped. past the ceiling below
Thunderstore did not answer, so BepInEx could not be fetched.
BepInEx is already being written. Try again in a moment. one write at a time
Set a valid server .exe path before installing BepInEx. there is no install to write into yet
There is no misplaced BepInEx folder to remove.
Something has BepInEx.dll open, so nothing was written. Stop every server on this install, close anything reading that folder, and try again. a locked file, caught before the first byte moved
The BepInEx here (5.4.23.5) is newer than the pack Thunderstore offers (5.4.22.0), so nothing was written. answer the downgrade question to go ahead
BakaLoader did not put the BepInEx that is beside this server, so nothing was written over it without asking you first. the press never went through the question
Another mod manager drives the BepInEx beside this server, so nothing was written over it. Writing here would swap the whole mod set that tool looks after.
The BepInEx here (6.0.0.0) is not the framework this pack carries, so nothing was written over it.
There is a BepInEx beside this server that BakaLoader does not recognise, so nothing was written over it.
This server's BepInEx core is a link to the install it shares, so the write belongs on that install instead. press it on the base install
There is no saved copy of BepInEx here to put back. nothing has been replaced on this install yet
BepInEx could not be written here. The log has the reason your machine gave, and a copy of anything that was replaced is in the backups folder beside the install. the one refusal that can come after files have moved
Thunderstore has taken BepInEx 5.4.2400 down, so a restart will not put it in. The install was left exactly as it was. somebody pulled that version

How it works

Which pack, and from where

The package is denikson-BepInExPack_Valheim, the one the Valheim community uses, fetched from its Thunderstore page:

https://thunderstore.io/c/valheim/p/denikson/BepInExPack_Valheim/

With the switch off, the install dialog puts that address in a box you can edit. Another link has to be a BepInEx pack of the same shape or nothing is written, which is the That download is not a BepInEx pack refusal above.

The install runs through seven steps and the progress dialog names each one as it happens: asking Thunderstore which pack is current, downloading the pack, checking what came down against what the site listed, unpacking, writing the files beside the server, sharing it with every server on this install, and done.

A download is capped at 50 MB and follows at most five redirects. The real pack is under a megabyte, so the ceiling is there to stop a link that answers with a hundred gigabytes rather than to be a limit anyone meets.

What is written, and what is not

BakaLoader writes an allow list rather than emptying a folder and unpacking over it. Exactly these go into the install:

winhttp.dll
doorstop_config.ini          only on a first install, or a loader generation change
.doorstop_version
changelog.txt
BepInEx\core\                the whole folder
BepInEx\config\BepInEx.cfg   only when there is not one already

Nothing else in the archive is copied, including the archive's own manifest.json and icon.png, which belong to Thunderstore rather than to the install, and the Linux and macOS files the pack also carries. Your plugins, your patchers and your config files are never touched by any of this.

A pack that grows a new top level file writes nothing new until this list grows with it. That is deliberate and it is the opposite of the rule the isolated install copier uses, because these two are doing different jobs: that one copies an install the host already has, this one unpacks an archive off the internet into the folder a server runs from. The other direction is handled too: a file on this list that is on disk and that the new pack does not ship is moved into the backup, so the loader files stay one coherent set.

Every file on that list is copied into the backup before it is overwritten, and the core is not copied at all: it is moved. The old BepInEx\core is renamed into the backup folder, which is both the backup and the removal in one step, and the new core is renamed into its place. The three most recent backups are kept. Three is enough to step back from an update that went wrong and past the one before it, and past that the pack it came from is gone from Thunderstore's current listing anyway.

When nothing needs changing

Most hosts who say yes are already on the pack Thunderstore is offering. After the download and before anything is written, BakaLoader compares what the pack would put down against what is on disk, file by file, by content: every file under core except the .pdb and .xml files that carry no code, plus winhttp.dll and .doorstop_version. Your doorstop_config.ini is yours and is not compared.

If they match, the only thing written is the note, with the hashes read off your own files, and the toast says so:

The BepInEx here already matched pack 5.4.2350 file for file, so BakaLoader took it into its care without writing over anything.

No backup, no swap, nothing moved. That is the common case and it costs an install nothing.

The same version with different bytes is not that. A core somebody has patched or pinned reads as a framework BakaLoader did not put down, and a restart leaves it alone.

When somebody else writes here too

The note lists every file BakaLoader wrote and what each one held. On a read, those files are compared against it. A file that has gone is the antivirus case: the row names it and offers to put it back, and a start or a restart window puts it back on its own. A file that is there, readable and different is somebody else writing to this install, and that is a different thing entirely: BakaLoader gives up ownership rather than fighting for it. The note is set aside as .bakaloader-bepinex.json.stale, the row goes to Outside with the drift sentence on it, and no restart writes there again. That is what stops two tools overwriting each other for ever.

Three things keep that from firing by accident: a file that cannot be read is never counted as changed, the comparison never runs while a write is in flight, so BakaLoader cannot read its own half finished swap as somebody else's, and editing BepInEx.cfg or doorstop_config.ini is not drift because neither is on the list.

A manual write you confirm takes the stale note away and puts the install back in BakaLoader's care.

When a write does not finish

Before the swap, BakaLoader writes a mark at BepInEx\.bakaloader-bepinex-writing holding the name of the backup folder, and holds that file open with no sharing for the whole write. It is deleted once the note is down.

A mark left behind with nothing holding it means the last write stopped part way: the machine lost power, or the process was killed between the two renames. The next status read, the next start and the next restart window all finish the job, putting the backup back. A mark over a core that is perfectly whole puts nothing back, because putting an older core back over a good one would be the damage rather than the repair. It clears the mark, and if the note beside the install no longer describes the files that are there (the write got the loader in and could not finish the note), the note goes too, so the install reads as Outside until you press Update, rather than claiming a version it cannot vouch for.

Because the file is held open while the write runs, a status read a second into a write can tell a write that is happening from one that stopped.

The marker file

A correct BepInEx install leaves nothing behind that names the pack it came from. The pack's own manifest.json is a top level entry in the archive and every reference installer deliberately refuses to copy it, so after a by the book install the only version on disk is the framework's own assembly version, and the pack does not follow it: pack 5.4.2350 ships BepInEx 5.4.23.5, and the pack moves on its own whenever the community changes something around it.

So every install BakaLoader makes writes its own note at the root of the install:

.bakaloader-bepinex.json

It holds the shape of the file, who wrote it (BakaLoader 1.2.0), the package as Thunderstore names it, the pack version, when it was installed, the address it came from, and every file that install wrote with a hash of what it held.

That file is the entire difference between Looked after and Outside. It is also why a note dropped in by hand does not work: BakaLoader believes it only when the shape is one this build knows, the writer name starts with BakaLoader, and a package and a version are both named. Anything else is read as no note at all, which puts the row back to Outside and costs you nothing.

What counts as installed

Two things, not one. BepInEx\core\BepInEx.dll has to be there, and so does winhttp.dll beside the server executable. Either one missing and nothing loads, so either one missing means not installed.

That matters because the older build asked one question, whether BepInEx.dll existed, and got three states wrong with it. A BepInEx 6 core read as nothing installed. A folder of files with the core assembly renamed read as nothing installed. A BepInEx.dll quarantined by antivirus read as nothing installed. In all three the install path would then have written a BepInEx 5 pack over whatever was really there.

Now:

  • BepInEx\core exists, holds files, has no BepInEx.dll and has no note BakaLoader trusts: that is Unrecognised. Nothing is installed or cleared there on its own, and a press asks first.
  • The same folder with a BakaLoader note beside it is Damaged: BakaLoader's own install with the core assembly gone, which it will put back.
  • A note listing files that are not on disk is Incomplete, and the row names the file.

The load check

There is no flag to get wrong. On Windows the loader is winhttp.dll sitting beside the executable and being picked up by the DLL search order, so BakaLoader never passes the server a loader argument and correspondingly gets no answer back about one.

The one thing BepInEx does leave is BepInEx/LogOutput.log, which its preloader rewrites at every launch. So the check is a file comparison: a log whose last write is older than the process that should have written it means the preloader never ran.

There is a grace of ninety seconds after the start, because the log is written a moment into startup rather than at the instant the process is created. Ninety seconds is far longer than that takes and far shorter than a host would tolerate wondering. Before the grace is up the absence means nothing and no row is raised. A log written on the same tick as the start counts as loaded, since a filesystem that stamps to the second can land both together and calling that a failure would raise the row on a healthy server.

The unattended step

The step that moves BepInEx runs inside the restart window, at every restart that has one. The server being restarted is down by then, so the only question left is whether any other server on the install is up.

It decides one of three things:

  • Skip when you have not said yes, when BepInEx is not installed at all (a server about to come up with no loader is handled by the start path instead, where the failure can be reported against the start that needed it), when Thunderstore did not answer, or when nothing newer is out. An unknown newest version is never a reason to write. It also skips an install another mod manager drives, one whose core is not a BepInEx 5, one holding files BakaLoader does not recognise, and one somebody else has written to since BakaLoader put it there.
  • Defer when there is something newer and another server on this install is up. The condition bar says so.
  • Apply otherwise, which includes putting back a file the note lists that has gone missing even when the version has not moved.

After the download it can still decline, and the row says which of these it was: a pre-release, a package Thunderstore has flagged as one to move away from, a pack listed less than 72 hours ago, a download with no published size to check it against, or an install newer than the pack.

If the step throws for any reason, the restart still happens and the row says what did not land. The world coming back up matters more than the loader being a version behind.

What you are told afterwards

A restart happens with nobody watching, so a line in the log is not telling anybody. What the last unattended write came to stands on the condition bar until you close it:

BepInEx was updated at a restart

BepInEx went from 5.4.2350 to 5.4.2400. A copy of every file that was replaced is in BepInEx\.bakaloader-bepinex-backups\20260920-011500.

and for one that wrote nothing:

BepInEx was left as it was

followed by the reason, every one of which ends with the install untouched.

One of those reasons is new in 1.2.1, and it is new because it used to be silent. A repair puts back files the note says are missing, and to do that it fetches the exact pack the note names. When Thunderstore has taken that pack down there is nothing to fetch, and the whole thing was swallowed as an ordinary "the site did not answer", which is deliberately not reported. So every restart asked for a pack that is not there and the install stayed broken with nothing said. It now has its own row and its own offer:

BepInEx was left as it was

This install was written from BepInEx 5.4.2350, and Thunderstore does not serve that pack any more, so the files that went could not be put back. The pack it serves now is 5.4.2400, and Update takes that one instead.

Update to the current pack

Pressing it takes the pack Thunderstore does serve. The row on the Mods page offers the same thing: in this one state it draws an Update beside Put the missing files back, because a repair can only ever ask for the pack the note names and that pack is gone. Either press carries your answer to that question with it, and a write over an install BakaLoader did not make still asks you first.

and for a write that stopped part way and was undone:

A BepInEx write here did not finish

A write to BepInEx stopped part way, so BakaLoader put the copy from before it back. The loader here is the one you had, and nothing was updated.

That last one is its own sentence on purpose. It is not an update: the loader you end up with is the one you started the day with, and there is no new backup folder to send you to, because putting a copy back over a core that had already gone replaces nothing.

A Thunderstore that did not answer is deliberately not one of those. Nothing was written and nothing is wrong with your install; it was a bad minute on the internet and the next restart asks again.

A write you press is answered with a toast rather than a standing row, because you are looking at it, and it says which of the five things happened: the version moved and where the old files went, a saved copy was put back, the pack was already the current one, an install already matched the pack so only the note was written, or a rule refused and why.

Questions people ask

I updated and I already have BepInEx. What just happened to it? Nothing. Not one byte is written until you answer the question with a yes, and if you answer no, or never answer at all, BakaLoader never touches it. From 1.2.1 it does not ask Thunderstore anything either until that yes.

BakaLoader installed this BepInEx, so why does the row say Outside? Most often because a write here did not finish and the note beside the files was never completed. The row says so in its own line now, and pressing Update to the current pack writes the pack again and settles it. See A write that did not finish.

I installed BepInEx with Gale or r2modman. Will BakaLoader fight me for it? No. BakaLoader reads your doorstop_config.ini now, sees that it points into that tool's profile folder, and leaves the install alone at every restart whatever the switch says. The row says Another tool. There is still a button, but it asks first and tells you it would swap the whole mod set that tool looks after.

I set BepInEx up by hand and I want to keep doing that. Answer no to the question, or turn the Upkeep switch off. The row stays, the version stays on show, and the only thing that moves the loader is a button you press, which asks before it writes.

Why does the row look the same on all my servers? Because it is the same BepInEx. See Multiple servers for how the sharing works.

Can I keep one server on an older BepInEx? Not on a shared install. Give it a fully isolated install when you create it, and it still shares core with the base, so the honest answer is no. Two BepInEx versions on one machine means two separate installs of the game server.

It says a pack is waiting and never goes in. Something on that install is always up. Stop every server on it once and the next restart window writes it, or press Update to the current pack on the row while they are all stopped.

It says a pack came out too recently. That is the 72 hour wait. A restart will take it once the pack has been listed three days; the button takes it now.

My BepInEx broke and I want the old one back. Press Put the saved copy back on the row. If the row is not offering it, there is no saved copy on that install, which means BakaLoader has not replaced anything there yet.

Does removing a mod ever remove BepInEx? No. denikson-BepInExPack_Valheim and ValheimModding-HookGenPatcher are never listed in the mod table and cannot be removed there. See Mods.

Where do my mod configs live through all this? BepInEx/config, and the only thing the install path ever writes there is the pack's own BepInEx.cfg, and only when there is not one there already. An existing one is never touched. The other file BakaLoader writes there is its own companion plugin's, and from 1.2.0 that rewrite carries your [Spawning] section across. See Players (Vikings).

Clone this wiki locally