Repository navigation
Spawner 2.2.0
Spawner is an open source, self-hosted preview environment manager: a copy of your app for each git branch, on your own server, with its own URLs, database and logs, for teams and their coding agents. Documentation, changelog.
A harder Spawner: environments kept from the cloud's metadata and the server's services, source repositories chosen by admins, Traefik 3.7 and releases you can verify. And its documentation, now on https://spawner.run.
- Environments no longer reach the cloud's metadata or the server's services: the code of a branch could read the credentials of the server from the metadata service of its cloud (AWS, GCP, Azure, Hetzner, OVH...), or call a service listening on the server. A new
spawner-firewallcontainer loads rules for every container of Docker's bridge networks: the metadata addresses are refused, but for DNS, and the server answers on DNS, HTTP and HTTPS only. The installer also has Docker load them before it starts, so that they hold from boot on: after an update from the dashboard, runinstall.sh --upgradeonce on the server for that part. - Source repositories are chosen by admins: the other sources of
spawner.yamlmust come from repositories listed in the project (Projects, Edit, "Source repositories");spawner uprefuses the others before sending anything. A branch could otherwise make Spawner clone any repository its deploy keys reach, or an address of the internal network. The upgrade lists for each project the repositories its live environments already use. - Traefik 3.7: Traefik 3.5, which no longer gets security fixes, gives way to 3.7; upgrading Spawner pulls it. Traefik 3.7 embeds lego v5, which dropped the DNS providers azure (use azuredns), dnspod, googledomains, cloudxns, brandit, iwantmyname and iij (use iijdpf): a server set up with one of them switches before upgrading. Request headers whose name holds a character other than a letter, a digit or a dash (
X_Spawner_Preview,X.Forwarded.For) no longer reach applications, so an application that reads headers the PHP or CGI way cannot be handed one that Traefik removes or sets. - What you download can be verified: the images,
install.shand the CLI bundle of a release carry build provenance attestations (gh attestation verify install.sh -R Flosk6/Spawner). Theinstall.shof a release names the digest of its image, and updates from the dashboard read it there: both run the image the release built, whatever its tag points to later. Postgres and Traefik are pinned by digest. - Terminals and log streams close when their access goes: deactivating a user, changing their role or revoking a token now closes the terminals and the streams of logs they had open, at once, and an expired token's within 30 seconds.
- Uploads stop at their first limit: an archive is read as a stream that stops at the first file, byte or decompressed byte over the limits, so that a compression bomb costs no more than a legitimate archive; a person has 5 deploys of uploaded code waiting to start at most.
- A harder installation:
- The installer no longer makes a token of the installation with every scope (
SPAWNER_BOOTSTRAP_TOKEN) unless given one; installations that have one keep it until you empty its line in.env. - It refuses a version older than the installed one (its database only migrates forward;
--allow-downgradeafter restoring a backup), a data directory other than the installation's, and system directories; it never makes new secrets over an installation that lost them, never upgrades without a backup (it starts Postgres for it), writes.envanddns.envin one move, keeps a trailing=of their values, and no longer shows the DNS credentials typed. - The data directory is mode 700, rendered compose files and logs are readable by Spawner only, Traefik runs without capabilities but binding ports, Postgres and Traefik have memory limits, Spawner's code belongs to root, and Prisma's telemetry is off. A local install over HTTP warns that it serves every interface; the development stack listens on 127.0.0.1.
- git reaches GitHub, GitLab and Bitbucket with the host keys they publish, even the first time.
- The installer no longer makes a token of the installation with every scope (
- The installer warns below 4 GiB of memory, where no environment starts with the default settings, and names the settings to lower. It accepts an
/opt/spawnerholding only.envanddns.env, as when restoring on a new server. - Smaller hardening:
- Previews: a cookie a preview sets under the name of Spawner's no longer locks its visitors out; share links opened on public URLs never reach the application; the waiting page of a public URL no longer says why its environment failed; logging out takes the preview cookie off the browser; the cache of preview hosts is bounded.
- The dashboard's page has a Content-Security-Policy that runs its own scripts only; every answer carries
Referrer-Policy, and over HTTPSStrict-Transport-Security. - Accounts: passkeys must verify their user (a PIN, a fingerprint); names are unique, whatever their case; renames and linked GitHub accounts are audited, and every audit event records the address of its request; the CLI approval page says when and from where the login started. Removing someone from the GitHub organization does not deactivate them in Spawner: do it on the Team page.
- Compose: an environment has 4 CPUs and 4096 processes to share between its services (
SPAWNER_ENV_CPUS,SPAWNER_ENV_PIDS), as it has its memory, and no service gets raw sockets (NET_RAW). A file asking more CPUs or processes than that is refused. - Uploads: links are followed through the links they lead to once extracted.
- CLI:
spawner loginopens http and https URLs only; untracked.env-*,.npmrc, private keys and*.pemfiles stay home like.envfiles; a Dockerfile is read only when it is a regular file of 1 MiB at most; the MCP server fences what comes from the environment and tells the model to read it as data.
- The documentation moves to https://spawner.run, with new pages: a quickstart from a fresh server to a first environment; previews for pull requests from GitHub Actions and GitLab CI; the two example projects; troubleshooting. Also new: every key of
spawner.yamland the codes of a refused.spawner/, what an application needs to run behind Spawner, every option, variable and error code of the CLI and what each MCP tool takes and returns, VS Code with GitHub Copilot, Gemini CLI and Devin Desktop for agents, and what Spawner does not do. - Corrections to the documentation: the minimum memory is 4 GiB, not 2; the compose policy listed
platformunderbuildas allowed, which Spawner refuses; services withoutmem_limitshare what is left of the environment's memory, up to 512 MiB each; public URLs (auth: none) never wake a sleeping environment; with GitHub login set to an organization, its members get an account at their first login, without an invitation;admin.js invitelinks last 24 hours; the Dockerfile example of "Dockerfiles that share their layers" builds again; removing Spawner and the local install download the installer from the release, as upgrades do. spawner initlinksspawner.yamlto https://spawner.run/docs/manifest/ instead of an outdated branch of the repository, and the instructions it writes for coding agents say what to do on exit code 6. The dashboard's Docs button opens https://spawner.run/docs/.spawner loginsays a 90-day token expires in 90d, not 89d 24h.- The MCP server is listed on the official MCP Registry as
io.github.Flosk6/spawner, and spawner-cli on npm links to https://spawner.run. Release pages say what Spawner is, give the install and upgrade commands apart, and show how to check the files againstSHA256SUMS.
Upgrading from 2.1.0: update from the dashboard or with install.sh --upgrade, then run install.sh --upgrade once on the server, so that Docker loads the firewall rules of the environments before it starts any container (an update from the dashboard installs the firewall container, not Docker's part). Before upgrading, a server set up with a DNS provider that lego v5 dropped switches to another one (see Traefik 3.7 above). The upgrade lists, for each project, the repositories its live environments already take sources from; new ones need an admin. An installation from 2.1 or before keeps its bootstrap token until you empty its line in /opt/spawner/.env.
Install
On a new server:
curl -fsSL https://github.com/Flosk6/Spawner/releases/download/v2.2.0/install.sh | sudo bash -s -- --version 2.2.0Upgrade
From the dashboard: Update to 2.2.0 on the System page. From the server:
curl -fsSL https://github.com/Flosk6/Spawner/releases/download/v2.2.0/install.sh | sudo bash -s -- --upgrade --version 2.2.0Both back the database up first, and go back to the previous version if the new one does not start.
Files
install.sh: the installer of this version.spawner: the CLI and MCP server in one file, for Node.js 20 or later. Ornpm install -g spawner-cli@2.2.0.SHA256SUMS: their checksums.- Image:
ghcr.io/flosk6/spawner:2.2.0(sha256:1f045be15c39aacc36fab3b86cc19fa6fb028b8daf036bb4a3d0ba6687dd8f42, linux/amd64, linux/arm64).
Verify
The image, install.sh and spawner carry build provenance attestations from this repository's release workflow:
gh attestation verify install.sh -R Flosk6/Spawner
gh attestation verify oci://ghcr.io/flosk6/spawner:2.2.0 -R Flosk6/SpawnerOr compare the files with their checksums: curl -fsSL -O https://github.com/Flosk6/Spawner/releases/download/v2.2.0/install.sh -O https://github.com/Flosk6/Spawner/releases/download/v2.2.0/SHA256SUMS && sha256sum -c --ignore-missing SHA256SUMS