Install DCS World Standalone on Linux, and keep it working.
Status: pre-alpha. Every command works —
check,install,launch,patch,verifyandreport— including finding the DCS installs you already have, installing DCS itself through its own updater, and launching it to confirm it actually flies. The design is settled and the work is broken down in issue #1. There is no usable release yet. Everything below describes the intended tool.
Getting DCS running on Linux is not one problem, it is a pile of small ones: which Proton build, which winetricks verbs, which DLL overrides, and a handful of fixes for bugs that only show up on Linux. The community has solved all of it, but the answers are scattered across forum threads, wiki pages and half-maintained gists — and they go stale.
Worse, DCS's own updater overwrites those fixes every time it runs. Getting the game working once is achievable. Keeping it working is the part that wears people down.
dcs-linux-installer collects the known-good setup into one tool, applies the fixes, notices when a DCS update has reverted them, and puts them back.
It is aimed at anyone running Linux, not just one distro. Immutable systems like Bazzite and SteamOS are first-class.
- Install DCS World Standalone into a Wine prefix built with a pinned GE-Proton via umu-launcher
- Check your system and tell you exactly what is missing, with fixes that suit your distro
- Find DCS installs you already have — from Lutris, Heroic, Steam, or this tool
- Patch the known Linux-specific breakages, and re-apply them after DCS updates revert them
- Verify that DCS actually works, by reading its logs rather than assuming a process that started is a process that works
- Report a redacted diagnostics bundle you can paste into a bug report
- Head tracking — TrackIR and opentrack setup and permission checks
HOTAS configuration, VR, and SRS. Each is a large project in its own right. This tool installs the game and keeps it running.
DCS has an Integrity Check that hashes game files. Servers running pure-client enforcement reject clients whose files have been modified — and some of the necessary Linux fixes modify exactly those files.
So every patch declares whether it carries that risk. Safe patches apply by default. Anything that could cost you multiplayer access requires an explicit opt-in and says so plainly before it writes anything. Every patch is fully revertible back to a state that passes Integrity Check.
No silent trade of your multiplayer access for a single-player bug fix.
DCS is over 150 GB. Most setups put it inside the Wine prefix, which means Wine's most common repair — delete the prefix and rebuild it — also deletes the download.
Here the game lives on a path you choose, outside the prefix, mapped in. The prefix stays small enough to throw away and rebuild in seconds, and you can put the game on whichever drive has room.
# One-shot, no install
uvx --from dcs-linux-installer dcs-linux check
# Or install the command
uv tool install dcs-linux-installer
dcs-linux check # is this machine ready? what DCS installs exist?
dcs-linux install # build the prefix, then hand off to the DCS updater
dcs-linux shortcut # add a KDE/GNOME launcher for a prepared install
dcs-linux launch # start a prepared install without judging it
dcs-linux patch # apply the Linux fixes (IC-safe ones by default)
dcs-linux verify # launch DCS and confirm it actually works
dcs-linux report # diagnostics bundle for a bug reportRequires uv. It installs to your home directory without root, so this works on immutable distros too.
The first command to run. It needs no DCS install — it is the first thing to run — and reports a pass/fail row per check, each failure carrying the command that fixes it, chosen for your distro:
dcs-linux check # exits non-zero if anything blocking is wrong
dcs-linux --json check # same results, machine-readableIt reports your distro and whether its base system is immutable, your GPU and driver version, umu-launcher and the available GE-Proton builds, missing external tools, free disk space against what a DCS install actually needs, and whether your filesystem supports reflink snapshots.
Once DCS is installed it also checks the things that break it in ways the logs never mention — DLSS upscaling, the missing Segoe fonts the AH-64D needs, d3dcompiler_47, and whether the game and your saved games really do live outside the disposable prefix.
check also reports on head tracking, because on Linux it usually fails for a boring reason: the TrackIR is plugged in and your account is not allowed to open it.
┏━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ┃ Check ┃ Result ┃
┡━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ warn │ Head tracker │ TrackIR 5 at /dev/bus/usb/001/007; no udev rule installed, and │
│ │ │ this user cannot open the device, so nothing will move in the │
│ │ │ cockpit │
│ │ │ → printf '%s\n' 'SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", │
│ │ │ ATTRS{idVendor}=="131d", TAG+="uaccess"' | sudo tee │
│ │ │ /etc/udev/rules.d/70-trackir.rules && sudo udevadm control │
│ │ │ --reload-rules && sudo udevadm trigger # then unplug and replug │
│ │ │ the tracker │
│ warn │ opentrack │ a head tracker is connected but opentrack is not installed, so DCS │
│ │ │ has nothing feeding it head movement │
│ │ │ → flatpak remote-add --if-not-exists flathub │
│ │ │ https://dl.flathub.org/repo/flathub.flatpakrepo && flatpak install │
│ │ │ flathub io.github.opentrack.opentrack │
│ warn │ Head tracking in DCS │ nothing in the prefix points DCS at an NPClient bridge, so head │
│ │ │ movement will not reach the cockpit │
│ │ │ → in opentrack set Output to 'Wine' and point it at │
│ │ │ ~/dcs-linux/prefix │
└──────┴──────────────────────┴────────────────────────────────────────────────────────────────────┘
Installing a udev rule needs root, so the tool prints the command and never runs it. None of these rows can fail the check — DCS flies without head tracking — and with no tracker connected and no opentrack installed, all three simply skip.
Head tracking is the only peripheral in scope. HOTAS, joysticks and throttles are not, and detection is limited to the NaturalPoint vendor id so it stays that way.
Paths default to ~/dcs-linux, ~/.cache/dcs-linux/toolchain and ~/.local/state/dcs-linux; override with DCS_LINUX_ROOT, DCS_LINUX_TOOLCHAIN and DCS_LINUX_STATE (which also honours XDG_STATE_HOME). DCS_LINUX_GAME moves the game directory on its own — the same choice install --game-dir makes, but remembered, so every later command measures the right drive.
check also lists every DCS install on the machine — from Lutris, Heroic, Steam, or this tool — with its game directory, prefix, Proton build, edition and DCS version:
2 DCS installs
┏━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ┃ ID ┃ Install ┃
┡━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ → │ 778d8145 │ steam · Steam edition · DCS 2.9.28.26385 · GE-Proton9-20 │
│ │ │ game: /mnt/games/SteamLibrary/steamapps/common/DCSWorld │
│ │ │ prefix: /mnt/games/SteamLibrary/steamapps/compatdata/223750 │
│ │ 7976590f │ lutris · Standalone edition · DCS 2.9.30.1000 · ge-proton │
│ │ │ game: /games/DCS World │
│ │ │ prefix: /games/prefixes/dcs │
└───┴──────────┴─────────────────────────────────────────────────────────────┘
Nothing is written while discovering — your launchers can be running.
The ID is how you name one install to a command, and it is derived from the game directory, so it does not change when the prefix is rebuilt:
dcs-linux check --install 7976590f # report on that install
dcs-linux check --install 7976 # any unambiguous prefix worksWith one install found, or one of ours, it is used automatically.
Two phases in one command. First everything DCS needs except DCS: the umu-launcher zipapp, a pinned GE-Proton build, the Wine prefix, the winetricks verbs, and the mapping that keeps your game and your login outside that prefix. Then the handoff to the DCS updater, where you log in and choose modules.
dcs-linux install # into the default layout
dcs-linux install --game-dir /mnt/big/DCS # put the 150 GB somewhere else
dcs-linux install --rebuild # throw the prefix away and build it again
dcs-linux install --prefix-only # stop at a ready prefix, download nothingIt prints one line per step, and re-running it is safe: an up-to-date prefix is left alone and only the mapping is re-asserted.
The toolchain versions are pinned, not resolved — the umu and GE-Proton builds are the exact ones this project flew to a working mission, so a bug report always names a reproducible pair. They are recorded in .dcs-linux.json inside the prefix, along with the verbs and the launch environment.
--rebuild is the repair everything else rests on. It deletes the prefix and nothing else: the game directory and Saved Games live outside it, so your download and your ED login survive. If either one is somehow inside the prefix, the rebuild refuses rather than destroying it.
Before any download starts, install re-runs the checks that it cannot itself fix — no GPU, a missing external tool, not enough disk space, a game directory inside the prefix — and stops on those. Everything else check calls blocking on a fresh machine (no umu, no Proton, no prefix) is exactly what this command is about to create.
vcrun2019 is refused if you ask for it with --verb: it causes a system-wide RAM leak. Use vcrun2015 or vcrun2022.
The updater needs an Eagle Dynamics account and a module selection through its own GUI, so that part is yours. install prepares everything around it, opens it, and picks the install up afterwards.
You have to fetch DCS_World_web.exe yourself — the download page needs a browser session, so it cannot be fetched for you. Put it in the toolchain directory (~/.cache/dcs-linux/toolchain) or pass --installer PATH. It is checked before it is run, and the hash of what ran is recorded.
In the updater, set the install path to D:\ — that is your game directory, mapped in — and leave torrent/P2P enabled, which is roughly ten times faster. C:\ would put the whole install inside the disposable prefix, so install refuses to open the updater at all unless the mapping is in place.
The download is 150 GB and up, measured in hours. Interrupting is safe: close the terminal, reboot, and run dcs-linux install again — the updater resumes where it stopped, and nothing is recorded until the install is finished. After that the install is registered, so check, launch, patch and verify find it without you repeating --game-dir.
When an explicit --game-dir adopts an existing install, KDE and GNOME users are offered a desktop shortcut after registration succeeds. Scripts can choose without a prompt by passing --shortcut or --no-shortcut.
Once it is done, run dcs-linux patch apply — the Linux fixes are what turn an install that starts into one that flies.
Start a complete install whose prefix was prepared by this tool:
dcs-linux launch # the single eligible install
dcs-linux launch --install 7976 # choose by ID or game pathIt uses the pinned umu and GE-Proton runtime, the mapped game directory and Saved Games, the Integrity Check-safe launch environment, and --no-launcher. It waits for the complete umu/Proton/Wine process tree and cleans it up if interrupted. The DCS exit code is reported but not treated as proof that the game worked; use verify when you want the log judged.
Create the same KDE/GNOME desktop launcher later, without re-running install:
dcs-linux shortcut # the single prepared install
dcs-linux shortcut --install 7976 # choose by ID or game pathThe launcher runs dcs-linux launch --install ID, so it keeps using the prepared prefix and durable mappings. It downloads DCS World's pinned multi-resolution icon (256, 48, 32 and 16 pixels) from Steam's CDN, identified through SteamDB, stores it under your XDG data directory, and assigns the local image to the launcher. Re-running the command does not download or create a duplicate when both files are already present.
The question the rest of this is for: does it actually work?
dcs-linux verify # launch DCS, then judge the log it wrote
dcs-linux verify --no-launch # judge the last run, starting nothingIt opens DCS, tells you to fly to a real mission rather than stopping at the main menu, waits for you to quit, and then reads dcs.log. A process that starts proves very little here: DCS reaches the menu with no symbols in the Apache, with garbage MFDs, or having never authorized — and exits cleanly through all three. So verify reports per issue, each with its fix and the patch that applies it:
ok Launch DCS closed with exit code 0
ok dcs.log ~/dcs-linux/saved-games/DCS/Logs/dcs.log, opened 2026-08-02 21:30:03
ok Authorization ED authorization succeeded
FAIL Fonts a font was requested by no name at all — the missing Segoe font …
→ dcs-linux patch apply segoe-fonts
FAIL Crash DCS crashed with an access violation in CockpitBase.dll
warn Shaders DCS could not find a precompiled shader and rebuilt it …
ok Session DCS shut down cleanly
ok Upscaling OFF
Authorization failures caused by a wrong system clock are named as such rather than reported as a generic error, because nothing about the symptom suggests the clock. And a clean log is not a clean run: DLSS flicker never reaches dcs.log at all, so the static rule that catches it is applied here too.
check stays the fast answer and still launches nothing. What verify finds is also in the report bundle, under Last run.
A diagnostics bundle, as markdown, ready to paste into an issue or a forum thread:
dcs-linux report # to stdout
dcs-linux report --install 7976 # about one installIt carries the tool version, your distro and kernel, GPU and driver, umu and the Proton builds present, every install found, what verify makes of the last run, the graphics block of options.lua, and the parts of dcs.log worth reading — the header, any known-fatal signature, errors with the known-benign noise filtered out, and the tail.
It is meant to be safe to post in public: home and removable-drive paths, wine profile names, email addresses, Steam account ids, GUIDs and routable IP addresses are all replaced, keeping the shape of the path so it still reads as one. The ED credential (Saved Games/DCS/Config/authdata.bin) is never read at all. --no-redact turns redaction off, for a bundle you are keeping to yourself.
Redaction is pattern-based, so give the output a glance before you post it — and open an issue if something identifying gets through.
It is bounded — a healthy DCS log is 150 KB with several hundred ERROR lines, so it is excerpted rather than dumped — and it works on a machine where everything is broken and nothing is installed, which is exactly when it is needed.
The Linux fixes, applied, reverted, and put back after a DCS update undoes them:
dcs-linux patch # what is available, and what is applied
dcs-linux patch apply # apply everything applicable
dcs-linux patch revert # put the install back exactly as it was
dcs-linux patch apply segoe-fonts # or one patch by name
dcs-linux patch clear-shader-cache # delete the compiled shadersDCS_updater overwrites patched files on every update and repair, so a patch is never "done". Each applied file is remembered by its hash, so the tool can tell you a fix has been undone — check reports that as a failure, and patch apply puts it back. Applying something already applied is a no-op, so running it after every DCS update is the intended habit.
Backups and state live in ~/.local/state/dcs-linux/<install id>/, outside the install, because DCS_updater repair deletes files ED's manifest does not list and would otherwise destroy the only pristine copy. Revert restores exactly what was there before — including a file DCS itself later replaced.
Nothing is written unless the whole fix can be assembled: a patch that cannot find what it needs says why and touches nothing.
| Patch | Integrity Check | What it does |
|---|---|---|
segoe-fonts |
safe | puts a locally installed sans font into the prefix under the three Segoe names the AH-64D needs. Prefix only, so no hashed game file is involved |
voice-chat |
comments the voice-chat entries out of optionsDb.lua — a game file DCS hashes |
|
mfd-textures |
re-encodes the AH-64D MFD and sight textures with ImageMagick — game files DCS hashes |
A risky patch is never swept up by a bare patch apply, and --allow-ic-risk does not widen one: it consents to a patch you named, and the multiplayer cost is printed either way. check tells you whether the install currently carries such a modification, and patch revert gives multiplayer back.
Both risky patches are workarounds for symptoms that were not reproduced on 2.9.28.26385 — voice chat loads clean, and the sight renders correctly once shaders finish compiling. They are here for anyone who does hit them; on a current install they will tell you there is nothing to fix rather than modify anything.
clear-shader-cache is maintenance rather than a patch: it deletes DCS's compiled shaders in Saved Games, which DCS rebuilds on the next launch (that launch takes several minutes). Nothing to back up, nothing to revert, and always Integrity Check safe.
| Area | Decision |
|---|---|
| Runtime | Pinned GE-Proton via umu-launcher |
| Layout | Game outside the prefix, mapped in; prefix small and disposable |
| Install flow | Prepare prefix → hand off to the DCS updater GUI → patch → launch or verify |
| Discovery | Adopts existing Lutris / Heroic / Steam installs, not just its own |
| Patches | Backup plus state file, matched by content pattern rather than line number |
| Patch state | Stored in ~/.local/state/dcs-linux/, outside the install, where DCS_updater repair cannot delete it |
| Multiplayer | Per-patch Integrity Check risk; risky patches opt-in only |
| Interface | CLI subcommands with rich output, --json and --no-color, non-interactive flags throughout |
| Language | Python, managed by uv |
Full reasoning is in issue #1.
The tool targets many distros, GPUs and launcher layouts, but is developed on one machine. Bug reports are how coverage happens — the output of dcs-linux report is the most useful thing you can attach.
Work is tracked as issues under #1. Anything labelled ready-for-agent with no open blockers is available to pick up.
The package is not on PyPI yet, so the two commands under Planned usage do not work. From a clone, uv run builds the environment from uv.lock and runs the command without putting anything on your PATH:
uv run dcs-linux checkSource edits take effect immediately — it is an editable install. --version is the one exception: the number is baked at build time, so it stays at whatever the last sync saw. Run uv sync --reinstall-package dcs-linux-installer when the number has to be right; the behaviour is current either way.
uv run --directory <clone> dcs-linux … works from any directory, and uvx --from . dcs-linux … builds a throwaway wheel from local source — the closest thing to what a user gets from PyPI. There is no python -m dcs_linux.
Every command defaults to ~/dcs-linux, so on a machine whose DCS lives elsewhere check reports no install found and skips every install-dependent row. Point it at a real one:
export DCS_LINUX_ROOT=/path/to/dcs # expects prefix/, game/, saved-games/
export DCS_LINUX_TOOLCHAIN=/path/to/toolchain
uv run dcs-linux checkDoing that is worth more than it sounds. check and report write nothing, so they are safe to run against a real install at any time — and the first probe ever compared against a real prefix turned out to disagree with the hand-written fixture it was tested on.
What CI runs, in order:
uv run ruff check . && uv run ruff format --check .
uv run mypy
uv run pytestPush a version tag; that is the whole process.
git tag v0.1.0
git push origin v0.1.0The version number comes from the tag itself, so the tag and the published package can never disagree. Uploads use PyPI trusted publishing — there is no API token in the repository or in CI. See ADR-0005.
This project stands on work the DCS Linux community did first:
- ChaosRifle/DCS-on-Linux
- budderpard/DCS_Standalone_on_linux
- TheZoq2/dcs_on_linux
- Hoggit wiki — DCS on Linux
MIT
Not affiliated with or endorsed by Eagle Dynamics. DCS World is their trademark.