Running FiveM in Docker: What Containers Fix, What They Do Not, and Whether Your Server Needs Them

Running FiveM in Docker: What Containers Fix, What They Do Not, and Whether Your Server Needs Them

Somebody on your team rebuilds the server box after a disk failure and it takes eleven hours, because the exact set of packages, paths, permissions and one weird symlink that made it work was never written down anywhere. It lived in the head of a person who has since gone to bed.

That is the problem containers actually solve. Not performance, not isolation for its own sake, but turning "how this server is built" from folklore into a file you can read. FiveM in Docker is entirely workable, with a few sharp edges that nobody mentions until you hit them at two in the morning.

What you get, honestly

The wins are real but specific.

Rebuilding is fast and repeatable. The image describes the server, so a new box is a pull and a run rather than an afternoon. Updates become swapping an artifact version in the build and rolling forward. Rollback becomes running the previous image. Running two servers on one machine stops being an exercise in port juggling and directory discipline.

The things it does not give you are worth stating plainly. It does not make FXServer faster. It does not protect you from a resource that leaks memory. It does not remove the need to understand Linux, it just moves where you need to understand it. And it adds a layer between you and the process, which is fine when everything works and mildly irritating when you are trying to attach to a console at speed.

If your server currently runs fine under systemd, one person administers it, and it has never needed rebuilding, containers are a solution looking for your problem. Come back to this when you have two servers, or two admins, or a memory of that eleven hour rebuild.

The image: keep it boring

The pattern that holds up is a small Debian or Ubuntu base, the FXServer artifact extracted into a fixed path, and nothing else. Pin the artifact version explicitly in the build rather than fetching latest, because an image that builds differently on Tuesday than it did on Monday defeats the entire point of building an image.

Everything that changes goes outside the image:

The test for whether you have got the split right is simple: destroy the container, run it again from the image, and see whether the server comes back with its resources, its admins and its settings intact. If anything is missing, that thing was in the container when it should have been in a volume.

Networking is where people lose an evening

FXServer wants UDP on its game port and TCP alongside it, and it advertises an endpoint that clients connect back to. Put that behind Docker's default bridge network with port mapping and you can end up with a server that appears in the list, accepts a connection, and then hangs, because the endpoint being advertised is not the one that works from outside.

Host networking avoids the whole class of problem and is what most people end up on. You lose the tidy port isolation, which matters if you are running several servers, and you gain a server that behaves exactly like a normal install as far as the network is concerned.

If you do want bridge networking with mapped ports, map the same port number on both sides for the game port, map both TCP and UDP, and be explicit in the config about what endpoint the server should advertise. Then test from outside your own network, because everything looks fine from the host.

txAdmin adds a second port for its web interface. Do not publish that one to the world. Bind it to localhost and reach it over an SSH tunnel or a private network, for the same reason you would not put your game panel on a public port with a password you chose in a hurry. That is the same access control conversation as everything else on the box, and deciding who can reach what should happen before the container starts, not after somebody finds the panel.

The stdin trap

This one costs people a genuinely surprising amount of time. FXServer reads its console from standard input, and when standard input reaches end of file, it exits. Cleanly. No error, no crash, just a server that shut down for no visible reason.

Run a container without an attached terminal and that is exactly what you get: the server starts, runs for a fraction of a second, and stops. The logs show a normal startup followed by a normal shutdown, which is the least helpful possible combination.

The fix is to give the container an interactive stdin and a pseudo-terminal. In a compose file that is the two options everybody copies without knowing why, and this is why. If you are restarting a container in a loop and cannot work out why it will not stay up, check this before you check anything else.

Database, and the thing to not do

Run MySQL or MariaDB as a separate container on a shared network, with its data on a volume, and point your database wrapper at it by service name rather than by IP. Standard, works fine.

The thing to not do is put the database in the same container as the server because it is simpler. It is simpler right up until you want to restart the game server without taking the database with it, which is roughly weekly.

Put the connection string in an environment variable or a config file mounted at runtime, never baked into the image. An image containing your database password is an image you cannot share, cannot push to a registry, and will absolutely forget about.

Backups still have to be arranged. A volume is not a backup, it is a directory with extra steps. Dump on a schedule, keep the dumps somewhere else, and test a restore occasionally, because everything about player data surviving a bad day depends on the restore working, not on the backup existing.

Updating and rolling back

The workflow that makes containers worth the trouble:

  1. Build a new image with the new artifact version, tagged with that version.
  2. Test it on a staging container against a copy of the database.
  3. Stop the live container, start the new one against the same volumes.
  4. If it goes wrong, stop it and start the previous tag.

Step four is the whole reason to do any of this. A rollback that takes twenty seconds changes how willing you are to update at all, and servers that update willingly are servers that are not four artifact versions behind when a security fix lands.

Keep at least the last two or three image tags. Disk is cheaper than the evening you spend rebuilding an old artifact by hand.

Permissions, the other classic

Containers run as a user, and that user needs to own the files it writes. Get the user id mapping wrong between host and container and you get a server that starts, runs, and cannot write to its cache or its resource data, producing errors that look like corruption rather than permissions.

Set an explicit user in the image, make sure the volume directories on the host are owned by the matching id, and check it once at build time rather than debugging it later. If your server works as root inside the container and breaks as anything else, that is a permissions problem wearing a costume.

When systemd is the better answer

Plenty of good servers run as a systemd unit with an update script and a cron job, and they are not doing it wrong. Systemd gives you restart on failure, log capture, resource limits and start ordering, which covers most of what a single-server owner actually needs from containers.

Reach for containers when you have more than one server on a box, when more than one person administers it, when you need staging and production to be provably identical, or when the rebuild story matters to you. Stay on systemd when none of that is true, because the simplest thing that works is not a compromise. The wider version of that decision, when to add a layer and when to leave the machine alone, is the same judgement call that runs through deciding which resources are worth carrying at all.

The short version

Small image, pinned artifact, everything mutable in volumes. Use host networking unless you have a reason not to, and keep the admin panel off the public internet. Give the container a terminal or FXServer will quietly exit on its own. Separate database container, credentials at runtime, real backups. Tag your images so rollback is twenty seconds. Do all that and the eleven hour rebuild becomes a command, which is the only promise containers actually make.

More FiveM script guides

When a FiveM Resource Rollback Needs a Database Recovery Plan
Guide
When a FiveM Resource Rollback Needs a Database Recovery Plan
Load Testing a FiveM Server Before Launch Night: Simulating a Full City Without a Full City
Guide
Load Testing a FiveM Server Before Launch Night: Simulating a Full City Without a Full City
Running Multiple FiveM Servers: Dev, Main and Event Instances Without Doubling Your Workload
Guide
Running Multiple FiveM Servers: Dev, Main and Event Instances Without Doubling Your Workload
Guide published · Sep 03, 2026 Browse every FiveM article →