- push to main triggers a rebuild of the current version so build changes get tested; scoped to main so the job's pages/tag pushes can't loop it, and docs-only paths are ignored to avoid wasteful full rebuilds. - the decide step treats a push (like a manual force) as build=true even when the version tag already exists. - cron still handles new upstream binary releases as before. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
O3DE Flatpak
Repackage the official Open 3D Engine Linux release as a Flatpak so it can be installed on any distribution — not only Debian/Ubuntu.
A Gitea Actions workflow checks daily for a new O3DE release, builds the Flatpak,
and publishes it as a static OSTree Flatpak
repository on the pages branch of this repo. You add that as a Flatpak remote and
install / update like any other app.
Status: community / unofficial. O3DE is a large application (~15–18 GB installed); building and hosting it is heavy. Treat this as best-effort.
Installing (end users)
# Flathub provides the runtime O3DE needs
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
# Add this repo (replace <owner> with the Gitea account that owns the repo)
flatpak remote-add --if-not-exists o3de \
https://gitea.pc-heini.de/<owner>/o3de-flatpak/raw/branch/pages/o3de.flatpakrepo
flatpak install o3de org.o3de.O3DE
flatpak run org.o3de.O3DE
Later updates:
flatpak update org.o3de.O3DE
The repo is currently unsigned (no GPG). Flatpak will add it with GPG verification disabled. See Signing below to harden this.
How it works
| File | Purpose |
|---|---|
scripts/make-flatpak.sh |
The build. Unpacks the official o3de_*.deb into /app, bakes in Python, and exports an OSTree repo using flatpak build-init/build-finish/build-export — no flatpak-builder, no bubblewrap, no privileged container. |
o3de-wrapper.sh |
Entry point. Finds the versioned o3de Project Manager binary inside the sandbox and sets LD_LIBRARY_PATH. |
org.o3de.O3DE.desktop |
Desktop entry under the Flatpak app-id. |
org.o3de.O3DE.metainfo.xml |
AppStream metadata (version stamped at build time). |
scripts/get-latest-version.sh |
Resolves the latest .deb URL, version, and SHA-256 from o3debinaries.org. |
scripts/build.sh |
Download + build + test the Flatpak locally (wraps make-flatpak.sh). |
org.o3de.O3DE.yaml |
Equivalent flatpak-builder manifest — kept as an alternative for builders that have a privileged/bwrap-capable environment. Not used by CI. |
.gitea/workflows/build-flatpak.yml |
CI: detect new version → build → publish to pages → tag vX.Y.Z. |
The engine ships as a Debian package at a predictable URL
(https://o3debinaries.org/main/Latest/Linux/o3de_<ver>.deb). The build extracts
it (ar + tar) and copies the payload into the Flatpak's /app. The version
directory inside the .deb changes every release, so the wrapper discovers the
executable at runtime rather than hard-coding a path.
Want the full story? O3DE is an engine + SDK that compiles code and writes into its own install tree at runtime, while a Flatpak gives it a read-only
/app. Reconciling those two facts is most of the work here.docs/DESIGN.mddocuments every non-obvious decision, the read-only-/appproblems and their fixes, and how to debug/maintain this across O3DE releases. Read it before changing the build.
CI requirements (Gitea Actions)
The workflow targets a self-hosted act_runner. Because O3DE is large:
- Disk: budget 60 GB+ free. The build needs roughly 2–3× the installed
size (extracted payload in
build-dir+ a copy committed into the OSTreerepo/). The job deletesbuild-dirbefore publishing to cut peak usage, but it can still be tight. If builds fail on space, that's the first thing to check. - No privileged container required. The build avoids
flatpak-builder/bwrap and usesflatpak build-init/build-finish/build-export, which only touch files and the OSTree repo. A plain unprivileged job container works. - Runner label: the job uses
runs-on: ubuntu-latest. Change it if your runner is registered with a different label. - Token: publishing force-pushes the
pagesbranch and creates avX.Y.Ztag. The auto-providedGITHUB_TOKEN(withcontents: write) usually suffices. If your instance restricts it, create a Personal Access Token with repo write access and add it as a secret namedPUBLISH_TOKEN— the workflow prefers it automatically.
It runs on three triggers:
pushtomain— rebuilds whenever the build itself changes (always builds, even for an already-published version, so you can test your change). Docs-only changes (README.md,docs/**,LICENSE*,.gitignore) are ignored. Scoped tomainso thepages/tag pushes the job makes don't re-trigger it.- daily
cron— picks up a new O3DE binary release; only builds when the upstream version has no matchingvX.Y.Ztag yet, so most days are cheap no-ops. workflow_dispatch— manual run from the Gitea Actions UI, with an optional force rebuild.
Building locally
# flatpak + curl + python3/setuptools (to pre-build the o3de sdist)
sudo apt install flatpak python3-setuptools # or your distro's equivalent
./scripts/build.sh
Then install from the local repo/ and run:
flatpak remote-add --user --no-gpg-verify o3de-local repo
flatpak install --user o3de-local org.o3de.O3DE
flatpak run org.o3de.O3DE
Signing (recommended hardening)
The first iteration publishes an unsigned repo for simplicity. To sign:
- Generate a key:
gpg --quick-gen-key "O3DE Flatpak" default default never - Export the public key and add
GPGKey=<base64>to the generated.flatpakrepo. - Pass
--gpg-sign=<KEYID>to bothflatpak-builderandbuild-update-repo, and provide the private key to CI via a secret. Until then, users add the remote with GPG verification disabled.
Caveats & things to verify
These were confirmed by inspecting the v26.05 package (opt/O3DE/26.05/…):
- Layout is confirmed for now: the package installs everything under
/opt/O3DE/<ver>/, with the launcher atbin/Linux/profile/Default/o3deand ~270.sofiles beside it (hence the wrapper'sLD_LIBRARY_PATH). If a future release moves things, adjusto3de-wrapper.shand the manifest. - Runs against
org.freedesktop.Sdk, notPlatform. O3DE's package dependencies are a build toolchain (clang/ninja/cmake/pkg-config) because the engine compiles project code at runtime. Those live in the SDK. Users therefore pull the SDK runtime (larger than Platform) on install — Flatpak does this automatically from Flathub. - Python is per-user, not baked in. O3DE needs a Python venv under
~/.o3de(writable), keyed to the engine path, so it can only be created at runtime — the build can't bundle it. The Project Manager GUI does not create it itself, so the wrapper (o3de-wrapper.sh) runspython/get_python.shon launch when the venv is missing or stale. The first launch downloads Python and can take several minutes with no window on screen — that's expected; subsequent launches are instant. (cmake, whichget_python.shneeds, comes from the Sdk runtime.) - The o3de CLI install (read-only
/app). On first launch O3DE installs its owno3deCLI fromscripts/o3deinto the per-user~/.o3devenv. By defaultpython/get_python.shdoes an editable install (pip install -e), which writes anegg-infonext to the source under read-only/appand fails.get_python.shhas a built-in immutable-install hook (the Snap path): if a prebuilt sdist exists atscripts/o3de/dist/o3de-1.0.0.tar.gzit installs that (non-editable) instead. The build pre-creates that tarball (python3 setup.py sdist) while the tree is still writable, so at runtime the CLI lands in the writable venv and nothing is written to/app. (The build also patches everycmake/LYPython.cmaketo a non-editable install, covering the separate project-build CMake-configure path.) If a future release renames the package or bumps its version away from1.0.0, the build fails fast — those are the lines to re-check. - Engine-path resolution needs
SNAP/SNAP_BUILD. Because theo3deCLI is installed non-editably, its package lives in the per-user venv, so O3DE'smanifest.get_this_engine_path()(which walks up from the package__file__) would resolve into the venv and fail to findengine.json. O3DE has a built-in override for immutable installs: whenSNAPandSNAP_BUILDare set it returns$SNAP/$SNAP_BUILD. The wrapper sets them to the engine root. (Confirmed these vars are read only there — no other snap-specific behaviour is triggered.) - The Project Manager's own user folder is read-only (cosmetic). With no
project open, O3DE puts its engine-level user folder (asset cache, metrics,
UserSettings.xml) under<engine>/.../user, i.e. read-only/app. You'll seeCouldn't find a valid asset cache folder…,cannot write to temp file … UserSettings.xml, and a metrics warning at startup. These are non-fatal — the Project Manager runs fine; it just doesn't persist its own window/UI settings between runs. The documented override (/O3DE/Runtime/FilePaths/SourceProjectUserPath) does not help: O3DE recomputes that path from the engine root during bootstrap and overwrites a--regset. Real project data (projects you create, their caches) lives under your home directory and is fully writable, so actual work is unaffected. - Launcher icon. Shipped as
org.o3de.O3DE.png(the.debitself has only in-editor asset icons). It's kept inside the app so the running window/taskbar shows it, but removed from the exported set becauseflatpak build-exportvalidates exported icons in a bwrap sandbox that fails in an unprivileged container. Consequence: the host menu launcher icon is generic. A privileged runner would let us export it properly. - GPU / drivers: the renderer needs working GPU access. The manifest grants
--device=dri/--device=all; on some setups you may also want the matching GPU driver extension from Flathub. - Sandbox filesystem:
--filesystem=homelets the Project Manager create and open projects under your home directory. Tighten or widen to taste. - Hosting via raw branch URLs works because Flatpak fetches individual files
(
summary,config,objects/…). Gitea serves these from thepagesbranch. If you later put a real static web host in front of it, just changeUrl=in the.flatpakrepo.
Not affiliated with or endorsed by the Open 3D Foundation. O3DE is licensed under Apache-2.0 / MIT.