Flatout gives a Linux app its own website and its own signed Flatpak repository, from one Docker container. Upload a .flatpak bundle and the update reaches every install; edit the homepage in the browser and publish it when it looks right. RPMs and Debian packages get signed dnf and apt repositories too, and scripts and AI agents can do all of it through an API and an MCP server.
Features
- Editable homepage — every section's text, which sections show and in what order, every color in light and dark, the fonts, the type size and the images, with a live preview at desktop, tablet and phone widths and a draft that goes live only when you publish. Every published version can be restored. Or turn the site off and keep only the repository
- A signed Flatpak repository — bundles are imported, signed and published with static deltas by Flatpak's own tools; installs update through GNOME Software, KDE Discover or
flatpak update - dnf and apt repositories too — upload an
.rpmor a.deband it is signed into the site's dnf or apt repository, so installs update with the rest of the system; any other file, an AppImage or a tarball, becomes a download - Stable and beta channels — promote a beta to stable without uploading it again, bring back an earlier build, or end a channel with a message to its users. A beta can also ship as a separate app ID that installs beside the stable one
- Multi-architecture — one bundle per architecture; every user's Flatpak picks their machine's build, and the site and admin say when one architecture is behind
- Install files that write themselves — the
.flatpakref, the.flatpakrepo, the download button and every command in the install guide follow the current release, key and address - Install numbers without telemetry — read from ordinary repository traffic; no address is stored, only a daily-rotating hash that is deleted after 60 days
- An API and an MCP server — scoped, revocable tokens for CI and AI agents, with the whole API described in OpenAPI
- Encrypted backups of everything, restored by a fresh install's setup
- Proxy-friendly uploads — the admin sends bundles and backups in 90 MB pieces, so large files get through Cloudflare and similar proxies
- Accounts with a first-run setup wizard, optional Cloudflare Turnstile on sign-in, and light and dark themes
Install with Docker Compose
No clone needed — the image is on Docker Hub. Save this as docker-compose.yml in an empty directory:
# The project name, fixed so the container and its volume keep their names
# whatever the folder is called.
name: flatout
services:
flatout:
image: hyprlab/flatout:latest
container_name: flatout
ports:
# host:container. Change the left side if 8102 is taken on your host.
- "8102:8000"
environment:
# Session signing key. If unset, one is generated and kept in the volume.
- SECRET_KEY=${SECRET_KEY:-}
# Set to 1 when the site is served over HTTPS.
- SESSION_COOKIE_SECURE=${SESSION_COOKIE_SECURE:-0}
# How many reverse proxies sit in front (Cloudflare Tunnel, Caddy...).
- TRUST_PROXY=${TRUST_PROXY:-0}
# The largest bundle accepted, in megabytes.
- MAX_UPLOAD_MB=${MAX_UPLOAD_MB:-2048}
# Cloudflare Turnstile for the sign-in page. Usually set up in Settings > Security instead.
- TURNSTILE_SITE_KEY=${TURNSTILE_SITE_KEY:-}
- TURNSTILE_SECRET_KEY=${TURNSTILE_SECRET_KEY:-}
volumes:
# Everything: the database, uploads, the repository and its signing key.
- flatout-data:/data
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
flatout-data:Start it and open http://localhost:8102:
docker compose up -dThe first visit opens the setup wizard, which creates your account and names your app — there is no default account or password. The admin lives at /admin and the homepage at /. Put a reverse proxy with HTTPS in front for a public site.
To follow Flatout's own beta channel, change the image tag to :beta (betas can break things — back up first), or to a version such as :1.3.0 to pin one. You can also build from source: git clone the repo and run docker compose up -d --build — its docker-compose.override.yml is picked up automatically.
Getting started
The admin's Overview lists the steps, and each one ticks itself when Flatout sees it done:
- Name the app and give it an icon — Content › Name and links, and Images
- Choose the colors and fonts — Design
- Write the homepage — Content: every section's text, which sections show, and their order
- Create the repository's signing key — Signing and addresses. Download a backup of the secret key and keep it off the server: if it is lost, every install has to add the repository again
- Upload the first release — Releases
- Publish the site — until then visitors see a "coming soon" page
Publishing a release
A release is a bundle made by flatpak build-bundle, one per architecture:
flatpak-builder --repo=build-repo build-dir org.example.App.yml
flatpak build-bundle --runtime-repo=https://dl.flathub.org/repo/flathub.flatpakrepo \
build-repo org.example.App-x86_64.flatpak org.example.AppUpload it in the admin under Releases, on the stable or beta channel. Flatout reads the app ID, architecture and version (from the app's AppStream metainfo), signs it into the repository, regenerates the signed summary and static deltas, and the update reaches installed copies the next time they check.
From CI, make a token under API and agents with the releases scope:
curl -fsS -H "Authorization: Bearer $FLATOUT_TOKEN" \
-F file=@./org.example.App-x86_64.flatpak -F channel=stable \
https://app.example.org/api/v1/releasesOr hand Flatout a URL and let it fetch the bundle itself — handy for release assets already published on GitHub. RPMs, Debian packages and other files go to /api/v1/packages the same way.
Packages: dnf and apt
Beside the Flatpak repository, Flatout keeps a dnf repository for RPMs, an apt repository for Debian packages, and a place for any other download. An RPM is re-signed with the repository's key and indexed with createrepo_c; a Debian package goes into a signed apt archive with stable and beta suites. Users add the repository in one command and then get every later version through their system's own updates:
sudo curl -fsSLo /etc/yum.repos.d/myapp.repo https://app.example.org/rpm/myapp.repo
sudo dnf install myappsudo curl -fsSLo /etc/apt/sources.list.d/myapp.sources https://app.example.org/deb/myapp.sources
sudo apt update && sudo apt install myappThe .sources file carries the key inside it (apt 2.4+, i.e. Debian 12 / Ubuntu 22.04). The site's install dialog shows these commands automatically once a package is published.
AI agents and MCP
Agents that speak the Model Context Protocol can connect to /mcp with a token. The tools cover the same ground as the API: reading and changing the site section by section, media, previewing the draft as text, publishing, releases, promotion, rollback, packages and install numbers. With Claude Code:
claude mcp add --transport http flatout https://app.example.org/mcp \
--header "Authorization: Bearer YOUR_TOKEN"An agent's changes go to the draft like anyone's; it can only publish with a token that has the site scope.
Configuration
Everything is optional — Flatout starts with none of it set. Put values in a .env file next to docker-compose.yml, then docker compose up -d to apply:
| Variable | Default | Purpose |
|---|---|---|
SECRET_KEY | generated | Signs sessions; if unset, one is generated and kept in the volume |
SESSION_COOKIE_SECURE | 0 | Set to 1 when the site is served over HTTPS |
TRUST_PROXY | 0 | How many reverse proxies are in front — usually 1 behind one |
MAX_UPLOAD_MB | 2048 | The largest bundle accepted |
TURNSTILE_SITE_KEY / TURNSTILE_SECRET_KEY | empty | Cloudflare Turnstile for sign-in; usually set in Settings › Security instead |
ALLOW_REGISTRATION | 0 | Whether anyone can create their own account |
WORKER_MINUTES | 60 | How often housekeeping runs (pruning old stats); 0 turns it off |
APP_NAME | Flatout | What the admin calls itself |
DATA_DIR | /data | Where everything is kept inside the container |
Behind Cloudflare Tunnel, Caddy, Traefik or nginx, set TRUST_PROXY to the number of proxies in front (usually 1) — the install files, the sign-in throttle and the install numbers all depend on it — and SESSION_COOKIE_SECURE=1 with HTTPS at the proxy. Setting it higher than the real number lets clients forge their address. A CDN in front works too: the repository's summary and signatures are sent no-store, and content objects never change.
Example .env
Copy this to .env next to your docker-compose.yml and adjust:
# Copy to .env and adjust. Everything is optional: Flatout starts with none
# of it set.
# Session signing key. If unset, one is generated and kept in the data
# directory, so sessions survive a restart.
SECRET_KEY=
# Set to 1 when the site is served over HTTPS, so session cookies are Secure.
SESSION_COOKIE_SECURE=0
# How many reverse proxies sit in front of Flatout. 0 trusts no
# X-Forwarded-* header; a number higher than the real one lets clients spoof
# their address. Behind one proxy that passes Host through, use 1.
TRUST_PROXY=0
# The largest Flatpak bundle accepted, in megabytes.
MAX_UPLOAD_MB=2048
# Cloudflare Turnstile for the sign-in page. Usually set up in the admin
# instead, under Settings > Security.
TURNSTILE_SITE_KEY=
TURNSTILE_SECRET_KEY=Updating & backups
docker compose pull && docker compose up -dMigrations run by themselves at startup; a major version (2.0.0) means an existing install needs something done by hand, and the changelog says what.
Settings › Backup (admins only) makes one file with everything — accounts, the site and its media, the repository with every build, the signing key and the settings — encrypted with a passphrase the server forgets once the backup is made. To restore, start a fresh install and choose Restore from a backup in its setup. A backup is standard OpenPGP around a tar archive, so it opens without Flatout too: gpg --decrypt flatout-backup-….tar.gpg | tar -x.
AI notice
Flatout is built by a human maintainer who uses generative AI as a development tool. The maintainer decides what gets built, reviews the results, tests every release and signs off on everything that ships. The app itself contains no AI and makes no requests to AI services; its MCP server is there for agents you choose to connect.
Tech stack & license
Flask · SQLAlchemy · SQLite · gunicorn · flatpak / ostree · rpm + createrepo_c — no frontend framework. Free and open source under the GNU AGPL-3.0-or-later; Cantarell and Inter are under the SIL Open Font License. Full docs and releases on GitHub, images on Docker Hub.