Changelog
All notable changes to Envoryx are documented here. The format follows Keep a Changelog; versions follow Semantic Versioning (0.x: minor versions may change behaviour, patch versions are fixes only).
Release images: ghcr.io/envoryx/envoryx:<version> and :latest (newest
release). :main follows the development branch.
0.34.1 - 2026-10-12
Fixed
envoryx admin tokens(andcreate-token --project,revoke-token) failed with “the value was encrypted with a key Envoryx does not have” once a project stored a secret: the admin commands now load the secret key like the server.envoryx worker presetsoffered the presets of every runtime; it lists only those the project can run, like the web interface.- Global flags before the command (
envoryx --json project list) were refused as an unknown command.
0.34.0 - 2026-10-12
Added
- The licence texts of the third-party software in Envoryx: Third-party licenses on the
logo’s page, and
/usr/share/doc/envoryx/in the image.images/SOURCES.mdsays where the sources of the images’ GPL parts are.
0.33.1 - 2026-10-12
Changed
- Updated dependencies: the Docker client library (moby client 0.6.2) and the web interface’s libraries (TanStack Query, Lucide icons, react-i18next, Vite). No change in behaviour.
0.33.0 - 2026-10-12
Added
- Install vsdbg in a .NET project’s IDE section downloads Microsoft’s
debugger for VS Code into the project home with Microsoft’s own script,
shows its output and then the installed version; run again, it updates
it. Also as the action
dotnet:vsdbg. Envoryx still doesn’t ship vsdbg, whose licence allows it only with Microsoft’s tools.
0.32.0 - 2026-10-12
The rest of the administration on the command line: diagnostics, the audit log, Docker clean-up, instance settings and project domains.
Added
envoryx auditlists the audit log with the web interface’s filters (--user,--action,--project,--since,--until,--search) and--export csv|jsonlwrites every matching entry.envoryx diagnosticsprints what Settings → Diagnostics finds and exits with 1 when a check is in error (--strict: also on a warning), for monitoring jobs.envoryx docker imagesanddocker prunelist and remove the Envoryx images no project uses;docker orphansanddocker removedo the same for containers, networks and volumes projects left behind.envoryx settingsshows the instance settings andsettings set NAME=VALUE …changes them (host for project links, base domain, HTTPS redirect, retention, the SSH server’s keys and more);envoryx domain list|add|removemanages a project’s extra domains.
0.31.0 - 2026-10-12
More of Envoryx from the command line: workers and cron jobs, API tokens and instance backups.
Added
envoryx admin tokens,admin create-tokenandadmin revoke-tokenlist, create and revoke API tokens from a shell in the container, for scripts that set up a CI job or an assistant. API tokens still can’t manage tokens over the API.envoryx instance …handles instance backups from the command line: list, create, download, upload, delete, restore (with--waituntil Envoryx is back, and--key-filefor a backup made with another key), cancel a scheduled restore, and copy them to and from offsite targets.envoryx worker …andenvoryx cron …in the command line: list, add, edit, enable, disable and remove workers and cron jobs, restart a worker, see a job’s runs and their output, and run a job now.cron run --waitprints the output and exits with the command’s exit code, so a script or a CI job can wait for it.
Fixed
envoryx admin revoke-tokensrefuses arguments:revoke-tokens cirevoked every token instead of the one named.
0.30.0 - 2026-10-11
What a full test round with real projects in every stack found: a security gap in the hoster’s support access, restores that left newer tables behind, test runs that reached the development queue, and many smaller things across the runtimes, backups and the interface.
Security
- The hoster’s support could make a user an admin and create invitation and password-reset links, which kept working after the admins took their consent back. Support now can’t create or change accounts, roles, invitation or password links, single sign-on, the instance’s SSH keys or restore an instance backup; it sees those areas read-only.
Added
- Project containers trust Envoryx’s local CA, so an application can call
its own
https://<project>.test(webhooks, its own API): the runtimes getSSL_CERT_FILE,REQUESTS_CA_BUNDLE,CURL_CA_BUNDLEandNODE_EXTRA_CA_CERTS, PHPopenssl.cafileandcurl.cainfo. A variable the project sets itself wins; Java keeps its own keystore. ENVORYX_DEV_URL, the address of a project’s Node dev server; the Laravel template’svite.config.jstakes its origin and hot reload socket from it, so Laravel with the Vite dev server works behind the proxy.- Adding Redis, Memcached or RabbitMQ switches on PHP’s
redis,memcachedoramqpextension, as PostgreSQL doespdo_pgsql. - Instance backups record the instance ID; the first start after a
restore onto a wiped
/configtakes it over when the backup’s containers are on the host and no other Envoryx runs there, so the projects are found again. - A warning when a .NET project targets an older version than it runs.
- Backups, restores and copies of a Rails project include its
_queue,_cacheand_cabledatabases. - The interface translates template, action, service, addon and diagnostics texts, and the audit log names project changes in words.
Changed
- A dump is restored into an emptied database (for the databases Envoryx runs): a snapshot or backup restore left tables created after it in place, and the next migration failed on them.
- PHPUnit and Pest runs get the
<env>entries ofphpunit.xml: the project’s and Envoryx’s variables beat them before, so a test run dispatched onto the development queue and mailed into its inbox. - Workers run under Docker’s init and stop on SIGTERM: a PHP worker ignored the image’s SIGQUIT and every stop waited 30 s for the kill.
- A runtime update through the API keeps every setting it leaves out, for all runtimes; the read-only host ports of a fetched configuration are accepted when it is sent back.
- The operations tray shows your own operations (and Envoryx’s own to admins), not every user’s.
- An MCP client sees only the tools its token may call.
- CLI commands that need
--yesexit with 2 without it. - The TYPO3 template installs the scheduler extension and gives PHP 512M;
typo3 setupempties the database and flushes the caches first. - Sizes are shown in KiB, MiB, GiB and TiB, the plan’s disk space too.
- A removed managed instance joins its fleet manager again with a new
ENVORYX_FLEET_TOKEN.
Fixed
- Projects and platform:
- Switching a project’s Git branch never worked (
git checkout --read the branch as a path). - A branch environment whose pull failed was deployed and notified again on every poll.
- Renaming a project left its
_testand Rails databases and its backups under the old name. - Host names of projects and domains added after a project started didn’t resolve inside it.
- Memory-limit kills went unreported on Docker hosts that send no
oomevent. - Saving a registry image again after adding its login didn’t pull it.
- Build and pull errors of your own image came back as internal errors.
- A duplicated project started before its data was copied in, and a failed start could move a project’s HTTP port.
- The API refused
postgresas a database type and created PHP projects withdisplay_errorsoff. - An addon that injects no variables turned the project’s pages white; the pages now show an error instead of going blank anywhere, and the addon card lists only variables that reach the application.
- Switching a project’s Git branch never worked (
- Backups and operations:
- Stopping a slow project raised a false “needs attention”.
- An offsite copy of a deleted project’s backup was given up; its failure didn’t name the project.
- S3 errors showed raw XML; an old start error stayed on the dashboard.
- A backup sealed with a rotated key now names the key it needs, and a rotation at start is in the audit log.
- Orphaned networks and volumes showed 0001-01-01 as their date.
- Runtimes:
- PHP files ran in Craft’s
cpresourcesand inassets/andimages/; TYPO3 on nginx answered every page with 500 (SERVER_NAME was_). - The
.envimport saved unresolved${...}references such as the Laravel template’sAPP_URL=${ENVORYX_URL}as text. - The wizard showed the default memory_limit for Shopware and TYPO3.
- A Java version switch left classes a newer JDK compiled, so the server
crash-looped;
mvnwandgradlewof the Spring Boot template weren’t executable. - The Go and Sinatra templates’
/healthzanswered 204, the health check expects 200. - The Rails template’s mails linked to localhost:3000.
- dotnet watch injected a browser refresh that couldn’t connect.
- Go and Pest failures in the Tests section had no file and line.
- The SFTP card of non-PHP projects talked about PhpStorm and composer.
- PHP files ran in Craft’s
- Managed instances:
- The hoster’s offsite target stopped working for the customer when the plan left offsite targets out.
- An update to an image that couldn’t even start was never reported as failed.
- The interface showed runtimes the plan leaves out and offered “New project” and “Invite user” at the limit.
- Every plan load wrote settings changes into the audit log.
- Interface: the Resources section scrolled sideways on phones and repeated axis labels, Settings → Users was too wide, project names were cut at tablet width, the mobile menu ignored Escape, an invalid invitation link explained nothing, and some texts weren’t translated.
- Python and Node.js:
- The Python server waits until the venv has its server package and no
pip or uv run is filling it. After a version switch the documented
pip installaction was killed half-way when the server started against the half-built venv, and a project without a venv crash-looped and locked out the actions that would install it. - pytest failures in the Tests section are readable (no escaped colour
codes) and name the file and line; pytest is also found in uv projects
that list it under
[dependency-groups]or only inuv.lock. - A started project whose server isn’t up says it is starting or keeps crashing instead of telling you to start it.
- With pnpm or Yarn the Vite, Next.js and Nuxt dev servers listen on all
interfaces again: Envoryx put a
--before their host and port flags, which pnpm and Yarn 2+ hand on to the script, so Vite listened on localhost only and the project URL gave 502. - pnpm 11 and newer use the package cache too (they read
~/.config/pnpm/config.yaml, no longerrc). - A fresh pnpm or Yarn project offers its install and build actions before
there is a lockfile, and
yarn installfrom Actions orenvoryx project execmay writeyarn.lock(CI=1 made Yarn 4 refuse). - Node workers and cron jobs keep a
NODE_ENVthe project sets itself in development, like the dev server. - The Next.js debugging example in the IDE section puts the server process on the published port; Playwright failures name their file and line.
- The debugpy hint under Runtime no longer suggests starting the server under debugpy, which the IDE section warns against; the German, Russian and Ukrainian IDE text said the opposite of the English one.
- The Python server waits until the venv has its server package and no
pip or uv run is filling it. After a version switch the documented
0.29.1 - 2026-10-11
The new logo: Envoryx’s mark is now ASCII art, the same as in the Foundry film, in two colours that follow the accent colour.
Changed
- The logo on the sign-in, invitation and suspension pages, in the sidebar, the favicon and the home screen icon is now ASCII art in the accent colour and white.
0.29.0 - 2026-10-11
A small one: the site import recognises an instance backup, Python 3.10 is marked end of life, and the Go and web dependencies are up to date.
Added
- An instance backup uploaded under New project → Existing website (or with
envoryx import) is recognised and kept with the instance backups, where it can be restored, instead of being analysed as a website.
Changed
- Python 3.10 reached its end of life and is marked as such in the version choice; projects on it keep running.
- Updated Go dependencies (Docker client, SQLite, go-jose) and web dependencies (React Query, Vite, Vitest, lucide).
0.28.0 - 2026-10-11
Managed instances: a hoster who rents out machines with Envoryx limits each instance with a plan and runs them from a fleet manager, which pushes the plans, suspends an instance, signs in as support with the admins’ consent, takes and collects backups and updates instances with a rollback. Without a plan or a fleet manager nothing changes.
Added
- Backups for the hoster: a plan can name the hoster’s offsite target, which gets every instance’s daily instance backup (and optionally the scheduled project backups) in a folder per instance; the admins see it but can’t change it. The fleet manager can take an instance backup on request and sees the newest instance backups and their copies. See DEPLOYMENT.md, Backups for the hoster.
- The fleet manager of a hoster: with
ENVORYX_FLEET_URLand a one-timeENVORYX_FLEET_TOKENthe instance enrolls, keeps a connection to the manager open (it dials out, no open port needed), takes over the plan the manager sends and reports its version and use every minute. - Plans for managed instances: a hoster who rents out machines with Envoryx
limits each instance with
/config/plan.json- the number of projects and users, the disk space, the runtimes, the features (addons, custom images, branch environments, external services, offsite targets, IDE gateway) and instance settings it fixes. Envoryx picks up changes to the file while it runs, refuses what goes beyond the plan with a clear message, hides what the plan leaves out and shows the plan with its use under Settings → General. Without the file nothing changes. See DEPLOYMENT.md, Plans. - Support access for the hoster: an instance’s admins can let the hoster’s support sign in (for an hour, a day, a week or until taken back, under Settings → General). The fleet manager then opens a one-time login link; the support user counts towards no limit, can’t create tokens or keys, and its sessions end with the consent. Every support login is in the audit log. See DEPLOYMENT.md, Support access.
- Suspension of a managed instance: the hoster’s fleet manager can suspend an instance with an optional message. Its projects stop, every other operation is refused, the web UI shows the users only the hoster’s message, and the API, MCP, SSH and SFTP refuse. Lifting it opens the instance again without starting anything. See DEPLOYMENT.md, Suspension.
- Updates from the fleet manager: an instance pulls the requested version and replaces its own container through a short-lived helper container, which waits for the new version’s health check and otherwise brings the old one back (restoring a database the new version already migrated). The result goes into the audit log. See DEPLOYMENT.md, Updates from the fleet manager.
Changed
- Restoring an instance backup never takes the secret key, the instance ID, the plan, the suspension, the fleet enrollment or the result of the last update from the archive, also from one that carries them.
0.27.1 - 2026-10-10
Fixes from a test round of every restore path, upgraded from 0.24.0: a project restored from a backup, duplicated or branched keeps its resource limits and health check again, and the restore says what an old backup leaves out.
Fixed
- Restoring a backup into a new project, duplicating a project and creating a branch environment dropped the resource limits and the health check; the new project now gets them, as 0.25.0 said it would.
- The restore into a new project says when a backup is from before 0.25, which carried no workers, cron jobs, limits, health check or proxy rules, instead of promising them.
- A project that can’t be created because its name, identifier, directory or port is taken says which one and by which project.
- A backup whose project settings are unreadable is refused as such instead of failing with an internal error; one sealed with another instance’s key says so in one sentence.
- Backups of deleted projects shows the additional databases of a backup too; the project wizard hides its summary while an uploaded Envoryx backup is open; a backup fetched from an offsite target keeps the size of its copy instead of showing 0 B as the target’s last upload.
0.27.0 - 2026-10-10
Project backups on an offsite target come back as new projects, also of projects this Envoryx doesn’t have: after losing the host, every project on the target can be restored, with or without an instance backup.
Added
- Project backups on an offsite target come back as new projects, also of
projects this Envoryx doesn’t have (a lost host, a project made after the
last instance backup): Settings → Backups → Backups of deleted projects
→ From an offsite target lists every project on the target, and
Restore into a new project fetches the backup and restores it with the
settings it was made with.
envoryx backup recoverdoes the same from the command line; the API isGET /api/v1/offsite/targets/{target}/projectsandPOST /api/v1/offsite/targets/{target}/projects/fetch.
0.26.1 - 2026-10-10
Debugging .NET from VS Code and Rider works as the IDE section describes it: VS Code with Microsoft’s vsdbg, Rider with port forwarding switched on.
Fixed
- Debugging .NET from VS Code: the
launch.jsonin the IDE section used netcoredbg, which ignoressourceFileMap, so breakpoints set in the files on your computer never bound. It now uses Microsoft’svsdbg, which you install once in the .NET terminal (the IDE section has the command; the image can’t ship it under its licence), withrequireExactSourceoff, sincevsdbgrefuses the breakpoints of a remote file otherwise. netcoredbg stays in the image for other editors. - Rider’s Attach to Remote Process needs SSH port forwarding: the IDE section now says to switch on Allow JetBrains Gateway for this project first. Without it Rider showed the session as connected while the application hung. Switching it on now also counts for an SSH connection that is already open, so the IDE doesn’t have to log in again.
0.26.0 - 2026-10-10
A backup downloaded from Envoryx can be uploaded again under New project → Existing website and is restored into a new project from there, also on another Envoryx with the same secret key.
Added
- A backup downloaded from Envoryx can be uploaded again: New project →
Existing website recognises it (the
.tar, also gzipped) instead of analysing it as a website, keeps it as a backup of its project, or with the backups of deleted projects when that project is gone, and restores it into a new project.envoryx import <backup>.tardoes the same. On another Envoryx this works when it has the same secret key, since the project settings in a backup are sealed with it.
0.25.0 - 2026-10-10
A backup can now be restored into a new project, next to the old one or in place of a deleted project: Envoryx sets the project up as it was when the backup was made and imports the parts you tick.
Added
- A backup can be restored into a new project instead of over its own:
Restore into a new project next to each backup creates a project with
the settings the backup was made with (services, versions, variables
with secrets, workers, cron jobs, repository) and, as ticked, its
databases, files and objects, under a name, directory and host ports of
its own. Deleting a project keeps its backups, and Settings → Backups
→ Backups of deleted projects now lists them, so a deleted project
comes back the same way, also under its old name. The API is
POST /api/v1/backups/{project}/{backup}/restore-new,GET /api/v1/backups/orphanedandDELETE /api/v1/backups/orphaned/{project}/{backup}; all need admin access to the whole instance. - Backups now also record the workers, cron jobs, resource limits, health check, proxy rules and custom images of the project, so a project restored from them gets those back too.
Changed
- Restoring a database dump into a stopped project starts its database for the import and stops it again, instead of failing.
0.24.0 - 2026-10-10
The Spring Boot and Quarkus templates can now set a project up with Gradle (Kotlin DSL, with the wrapper) instead of Maven. Gradle keeps its dependencies in the project home, also when the package cache is shared, since its cache locks don’t work across containers.
Added
- The Spring Boot and Quarkus templates can set the project up with
Gradle (Kotlin DSL,
build.gradle.kts, with the Gradle wrapper) instead of Maven: the wizard has a Build tool field for them, the API takestemplateBuildTool(mavenorgradle).
Changed
- Gradle keeps its home (dependencies, wrapper distributions) in the
project home,
~/.gradle, instead of the package cache, and stays there when the package cache is shared. Gradle hands its cache locks over through a message on localhost, which doesn’t reach another container: with a shared cache and a Gradle server running in one project, Gradle in every other project failed after a minute with “Timeout waiting to lock journal cache”. The oldgradlefolder in the package cache can be emptied under Settings → Package cache.
0.23.1 - 2026-10-10
Envoryx runs as root and works in directories that belong to whoever
works in a project. A link or a named pipe put there could make it hang,
fill its memory, or change files outside the project when the project
started or a backup was restored. It now treats what it finds in a
project as untrusted. A project whose home holds a symbolic link where
Envoryx creates its directories (~/.config, ~/.cache) stops with an
error naming the link until it is removed.
Security
- Envoryx reads some of a project’s files itself (composer.json,
package.json, go.mod, pom.xml, a .csproj, config/database.yml,
.venv/pyvenv.cfg) and could be made to read without end: a link to
/dev/zeroin the project filled its memory, a named pipe hung the request. These files are now opened inside the project only, without blocking, and read only when they are plain files of at most 1 MB. - A symbolic link in the project home (
~/.config -> /config) made Envoryx, which runs as root, hand the directory it pointed to to the project user and create files in it when the project started. Envoryx now follows no link when it creates the project’s directories and configuration files; it stops with an error that names the link. - A restore without wiping first followed a directory link the project’s
owner had put into the project (
d -> /config) and changed the mode of the directory it pointed to. Restores, file backups and project copies now work through anos.Rootof the project, so nothing outside it is read, written or changed.
0.23.0 - 2026-10-10
Projects no longer share what one of them could tamper with. Each project now has its own package cache and its own JetBrains Gateway backends, so a developer of one project can’t plant code that another project installs or runs; where everyone is trusted, Settings → Package cache shares both again. The first install and the first Gateway start in each project download once more. Single sign-on connects an existing account only through its invitation link, never by name.
Security
- Single sign-on linked a provider account to an Envoryx account of the
same name while that account had an open invitation, a password reset
included. Where the provider sends no username, the name is the part of
the e-mail address before the @, so
bob@anywhere.examplecould take overbobduring a reset. Only the invitation link itself connects an account now: its page offers single sign-on (also for a reset), and the provider’s account is linked to the invited one whatever it is called there. Signing in from the login page never links by name. - The JetBrains backends downloaded for Gateway were shared by every
project and writable from each, so a developer of one project could
change the IDE backend another project runs. Each project now keeps its
own in its home; Settings → Package cache → Share the IDE backends
between projects brings the shared cache back where everyone is
trusted. Projects with Gateway download their backend once more after
the update;
/config/jetbrainscan be deleted unless you share again. - The package cache (Composer, npm, Yarn, pnpm, pip, uv, Go, Bundler,
Maven, Gradle, NuGet) was one directory for all projects and writable
from each. Composer, pip, Maven and others take a cached package
without checking it against the lock file, so a developer of one
project could plant code another project installs. Each project now has
its own cache; Settings → Package cache → Share the package cache
between projects brings the shared one back where everyone is trusted.
The first install in each project after the update downloads again, and
the sizes on that page add up all projects. Emptying the cache there
also empties the old
/config/cache.
0.22.1 - 2026-10-09
A security release; update soon if more than one person works on your instance. Over SFTP a developer of any project could reach Envoryx’s own files through a symbolic link, and with them the whole instance. A project admin could copy another project’s database into their own, and a page on another site could start a project action in a signed-in browser. Envoryx is now built with Go 1.27.2.
Security
- SFTP followed symbolic links out of the project: a link to
/made in the container (or committed to the repository) let any developer of a project read and write Envoryx’s own files over SFTP, the secret key and the database included, and from there take over the instance. SFTP now serves the project and home directories throughos.Root, so links are followed only while they stay inside. - Cloning a database checked access to the target project only, so a project admin (or an admin token confined to one project) could copy any other project’s database into their own and read it. The source now needs operate access too.
- A page on another site could start a project action or test run by sending the browser to its WebSocket address, since the run began before the origin was checked. WebSocket routes now refuse anything but a WebSocket upgrade from Envoryx’s own origin before they start.
- Built with Go 1.27.2, which fixes eleven vulnerabilities in Go 1.27.1’s
standard library that Envoryx’s code reaches, most of them in the
HTTP/2 server (crashes, memory and CPU exhaustion) and HTTP/1
connection handling. CI now runs govulncheck on every change and
weekly, and
make vulncheckruns it locally. - SECURITY.md lists
BUILDamong the socket proxy groups that project Dockerfiles need, and says plainly what a socket proxy can’t prevent.
0.22.0 - 2026-10-09
Fixes from the October test round. Saving a setting such as Xdebug no longer restarts the whole project, only the containers it concerns, and PhpStorm now hits breakpoints behind the proxy. The Tests section reads RSpec, Django, Vitest and Jest results instead of going by the exit code alone, Yarn 2+ and pnpm use the shared package cache, Quarkus’ Dev UI opens at the project URL, and the Laravel and Symfony templates point their app URL at the project. PHP projects ask for one restart after the update.
Added
- The Tests section reads the results of RSpec without
rspec_junit_formatter, Django’smanage.py testand Vitest or Jest behind a package.json test script from their output: counts, failed tests with file and line, and the message. Before, only the exit code counted. RSpec runs with the JUnit formatter get the line of each failed example as well. - Every run in the Tests section shows its output, past runs included (folded when the report explains the failure).
Fixed
rails teston Rails 8.1 showed failed tests without the line they start on (and errors without any location): Rails 8.1 prints two blank lines before the command that runs a test again.- Yarn 2 and later and pnpm downloaded every package again in each
project. Yarn’s global cache now lives in the shared package cache
(
YARN_GLOBAL_FOLDER), and pnpm’s store too: the project home gets~/.config/pnpm/rcwithstore-dir(written once, yours to change), since pnpm reads its store from no variable npm leaves alone. pnpm no longer creates a.pnpm-storedirectory inside the project either. - Xdebug breakpoints in PhpStorm were ignored for requests over the proxy
(“Cannot bind file … to the web server project”): PhpStorm matches the
server by host and port, the
php.xmlfrom the IDE tab had no port, so the server was taken as port 80 while the browser used 443. PHP containers now setPHP_IDE_CONFIG=serverName=<project host>, which names the server for browser requests and the command line alike, and the IDE tab shows the server port and writes it intophp.xml. - Quarkus’ Dev UI answered only
localhostand refused the project’s address with “Only localhost is allowed”. A Java server now getsQUARKUS_DEV_UI_HOSTSwith the project’s host name, a pattern for the other names under the base domain and the public host, so/q/dev-uiopens at the project URL (patterns need Quarkus 3.16 or later). - Links in mails and anything else a Laravel or Symfony project built
outside a request (queue workers, the scheduler, console commands)
pointed at
http://localhost. The Laravel template now setsAPP_URL=${ENVORYX_URL}in.env, the Symfony templateDEFAULT_URI=${ENVORYX_URL}in.env.local, so both follow the project address. Existing projects can add the same line. - Saving project settings stopped and started every container of a running project, the database and OpenSearch included, even when only Xdebug or another PHP setting changed. Envoryx now recreates just the containers the change concerns: a PHP setting restarts the PHP container, the PHP workers and the web server, the database and the other services keep running. PHP projects ask for a restart once after the update, and a changed Xdebug host in the settings now does the same.
0.21.0 - 2026-10-08
A security fix (see Security): PHP files that got into an upload
directory (WordPress’ wp-content/uploads, Drupal’s sites/*/files and
the like) could be run in the browser; restart your PHP projects after the
update to apply the new web server rules. The PHP images now ship
Node.js, so Shopware’s administration and storefront build in the PHP
container. A restore empties the project’s Redis, new Laravel projects run
their migrations, and Envoryx no longer hands out host ports that a program
on the host already uses. Crash-looping workers show as restarting and can
be restarted on their own.
Added
- Restoring a backup empties the project’s Redis afterwards: sessions,
object and page caches and queued jobs referred to the data from before
the restore (WordPress’ object cache, Shopware’s HTTP cache). The
restore dialog has a box for it, ticked by default; a stopped project’s
Redis is started for it and stopped again. An external Redis is left
alone. API:
flushRedis, default true. - The PHP images ship Node.js 24 LTS with npm and yarn (pnpm through
Corepack on first use). Shopware’s
bin/build-administration.shandbin/build-storefront.sh, which call php and npm in turn, run in the PHP container now, and so do the npm builds of Laravel’s Vite, Drupal and TYPO3 themes or WordPress blocks from the PHP terminal. Shopware projects get both build scripts as actions. A Node service still runs dev servers. The images grow by about 150 MB. - Workers can be restarted on their own from the Workers section (a queue worker keeps the code it loaded at its start), also by developers. The rest of the project keeps running.
Changed
- New Laravel projects run
artisan migrateonce they’re up, as Rails projects rundb:prepare: the sessions, cache and jobs tables are there before the first request, and the queue worker no longer restarts in a loop until someone ran the migration. A project created stopped still gets it from Actions.
Fixed
- Envoryx handed out host ports that a program on the Docker host already listened on (a JetBrains IDE on 30000 and 30001, a database installed on the host), and the project’s start failed in Docker. Envoryx now looks at the host’s listening ports through a short-lived container in the host network and skips them. A stopped project whose HTTP port was taken since moves to a free one at its next start; any other port in use is named in the error instead of Docker’s raw message.
- A worker or service whose process exited a few seconds after each start showed as running, since Docker lists it as running between its restarts. Containers Docker restarted three times or more, the last time within the last minute, now show as restarting, the project as partly running, and the worker says to look at the logs.
Security
- PHP files in upload directories ran like application code: a script
that got into
wp-content/uploadsor Drupal’ssites/default/filesthrough an upload form could be called in the browser. The generated Caddy, Nginx and Apache configs now refuse PHP below the upload and cache directories of WordPress (also Bedrock and subdirectory installs), Drupal, TYPO3, Shopware, Magento and Laravel and below/uploads, as the hardening guides of these CMSs recommend. Everything else there is served as before, Drupal’s image styles included. After the update every running project with a web server reports an older setup for its web container once; restart it to apply the rules. From now on a changed web server config always asks for that restart.
0.20.0 - 2026-10-08
The rest of the fixes from the application test round. The templates work behind the proxy out of the box (WordPress, Drupal, TYPO3, Rails, Django, Flask, FastAPI, Next.js, Spring Boot, Quarkus), Shopware and Craft get worker presets, and the SSH lockout no longer locks a whole office out after a few offered keys. SFTP and scp now start in the project directory instead of the tool home (JetBrains IDEs keep the home for their helpers).
Added
- Worker presets for Shopware’s message queue and scheduled tasks and for Craft’s queue. The add form starts with the preset of the project’s framework instead of Laravel’s queue worker.
- Static sites get SFTP for their files; a shell explains why there is none.
Changed
- SSH lockout: only guesses count (a wrong or unknown API token), not offered keys or empty passwords; failures expire after ten minutes; the lockout is per address and project, with a looser limit for the whole address. A locked-out client gets a banner saying why.
- SFTP and scp start in the project directory, like an ssh login.
- New WordPress, Drupal and TYPO3 projects and imported sites of these get
configuration that follows the project: WordPress takes its address from
the request (or the project URL) and finds the project’s Redis, Drupal
gets
trusted_host_patternsand adrush/drush.ymlwith the project URL, TYPO3 trusts the proxy, has a sender address and mails to Mailpit. - New Rails projects run
db:prepareonce they’re up, use Solid Cable in development, mail to Mailpit, log to stdout and let web-console answer through the proxy. Django mails to Mailpit, Flask uses ProxyFix, Next.js lists the project’s host names inallowedDevOrigins, Spring Boot creates its tables (ddl-auto=update), Quarkus builds the test database’s tables, Gin trusts the proxy, and the Python templates write a.gitignore. - uvicorn and gunicorn trust the proxy’s forwarded headers
(
FORWARDED_ALLOW_IPS=*). - The IDE section’s debugpy examples start debugpy in the reloader’s child process, so they work next to the running app server.
- Audit entries keep the target’s name, so they read right after the target is gone, and every action has a label.
- Docker resources of another instance on the same host, or from before 0.18, are counted apart from this instance’s orphans and no longer show a warning.
Fixed
- A duplicated WordPress sent wp-admin to the original’s address.
drush uliprintedhttp://default/...links.db:prepareleft the Rails queue database empty when the site had been opened first, and arails newwhosebundle installfailed was reported as ready.- Turbo broadcasts from Rails jobs never reached the browser in development.
- Next.js Fast Refresh never connected behind the proxy.
- FastAPI’s redirects left HTTPS.
- The Composer script worker preset stopped after 300 seconds.
- scp copied the file but exited with 1.
- Viewers saw Start/Stop/Restart on the dashboard.
- A branch environment couldn’t be given to a developer after it was created.
- The site import failed on
GRANTandCREATE USERlines in MySQL dumps. - Deleting an unhealthy project sent a “recovered” notice with its id.
- A new cron job skipped the minute the UI showed when it was saved.
- Restoring a backup failed on read-only files when Envoryx doesn’t run as root.
- Updating the Node service without
inspectunpublished the inspector. - The IDE section showed
<projects share>instead of the host path on bare metal. - The Tests page grew wider than the window.
- Container logs showed raw terminal colour codes.
0.19.0 - 2026-10-07
More fixes from the application test round. New projects get the current
runtime images, the PHP images bring the tools CMSs expect, and a dev server
that can’t start yet waits instead of crash-looping and locking you out of
the actions that would fix it. The Node container no longer sets
NODE_ENV=development for everything, only for the dev server, and the dev
server installs from your lockfile when node_modules is missing.
Added
- The PHP images bring ImageMagick (TYPO3 processes images with it), the
MariaDB and PostgreSQL clients (
drush sql:*,wp db,artisan db), WP-CLI aswp, common UTF-8 locales and theexifextension. PHP 8.5 grows from 1.07 to 1.24 GB. - Every runtime image has rsync, so
rsync -e sshworks. - The Node dev server installs the dependencies of a fresh checkout before it
starts:
npm ci,pnpm install --frozen-lockfileoryarn install --frozen-lockfile, whichever lockfile the project has. Without a lockfile it waits for an install from Actions.
Changed
NODE_ENV=developmentis set only for the Node dev server, no longer for the whole container. Builds from Actions, the terminal and cron jobs run without it; workers and cron jobs follow the dev server’s mode.- The generated php.ini sets
max_input_vars = 5000(PHP’s 1000 silently drops fields of big backend forms) and sizes OPcache for large vendor directories. - A project cloned from a repository takes the dev server script and preset
from its package.json instead of guessing
devwith Vite.
Fixed
- New projects, runtime version switches and new services pulled no image,
so they used whatever older build of the tag was on the host: PHP without
msmtp (
mail()failed), Ruby without libvips. They now get the current image; offline the local one is still used. - Adding a worker or changing a limit recreated every container whose image had been updated meanwhile, which restarted the database and emptied Mailpit. Only start and restart apply image updates now.
- Switching a Python, Go, Ruby, Java or .NET server off and on gave it a new host port.
- A dev server that couldn’t start crash-looped, and a crash-looping
container refused every action, including the install that would fix it.
The Node dev server now waits for package.json and node_modules, Python
servers and workers wait while
.venvwas built for another Python version, and an action on a restarting container says what’s going on. Rebuilding the venv lists the packages the old one had. npm run buildfrom Actions failed for Next.js and shipped React’s development bundle from Vite because of the container’sNODE_ENV.
0.18.1 - 2026-10-07
Fixes from the application test round. The important one: running PHPUnit from the Tests section in a Laravel project emptied the development database. PHP projects behind the proxy now know they’re on HTTPS, and Node, Python, Go, Java and .NET get a PostgreSQL URL their drivers accept. Restart running projects so their containers pick the changes up.
Fixed
- Tests no longer run against the development database. The
DB_*variables Envoryx injects beat the<env>entries ofphpunit.xml, so Laravel’sRefreshDatabaseranmigrate:freshon your data. PHPUnit and Pest now run against<database>_test, which Envoryx creates as the administrator before the run; Symfony, whose Doctrine recipe adds_testitself, ends at the same database instead of failing with error 1044 on MySQL and MariaDB. Django’s runner and pytest-django gettest_<database>created up front for the same reason. Java runs with a database URL that has parameters (?useSSL=false) used the development database too. - PHP sees requests that came in over HTTPS as HTTPS. TLS ends at the
proxy, and PHP-FPM saw plain http: Laravel’s login form posted to http,
Symfony’s toolbar and redirects were blocked as mixed content, the
Shopware installer and the TYPO3 backend redirected to http. The
generated Caddy, Nginx and Apache configs now set
HTTPS=on,REQUEST_SCHEME=httpsand the browser’s port for requests the proxy marks as https, so no trusted-proxy setup is needed. - Node, Python, Go, Java and .NET containers get PostgreSQL URLs as
postgresql://, which SQLAlchemy, Prisma, pgx and lib/pq accept, instead ofpgsql://. PHP keepspgsql://for Laravel and Doctrine, andDB_CONNECTIONstayspgsqleverywhere.
0.18.0 - 2026-09-30
Five security fixes (see Security), the most serious in the database browser, which let anyone who could operate one project open every project’s database. Several Envoryx instances can now share a Docker host without deleting each other’s containers. New object storage buckets are private and removing a service keeps its data unless you choose otherwise. Ruby projects report an older setup once after the update (new database variables); restart them when it suits you.
Added
- Go, Java and .NET projects warn when a project file needs a newer
version than the service runs, as Python does for a stale
.venv: thegoline ofgo.mod, the Java level inpom.xmlorbuild.gradle(.kts)(for example the Spring Boot template’s Java 25 on a project switched to Java 21), and the target framework of the project file or the SDK inglobal.json. Values Envoryx cannot read for sure (variables, parent POMs,Directory.Build.props, several target frameworks) give no warning.
Changed
- Orphans without an instance label (created by an older Envoryx, maybe another instance’s) are no longer removed automatically; the Docker page marks them older Envoryx and removes them on request. Orphans an instance labelled itself are still cleared after two passes.
- Django projects from the template serve their static files in
production mode: the settings get
STATIC_ROOT(staticfiles/) and WhiteNoise, so gunicorn no longer answers 404 for/static/and the “manage.py collectstatic” action works. Projects created before needwhitenoiseinstalled and those settings added by hand. - The Workers & cron section shows the command a .NET “Project” worker
really runs (
dotnet publish, thendotnet <dll>) instead ofdotnet run --project. - New object storage buckets are private.
storage: {}in the API, the Add object storage card, the MCP tool andstorage: trueinenvoryx.ymlused to create a bucket anyone could read through the project’s S3 host name; now only presigned URLs and authenticated requests work until Anyone may read objects is switched on ("publicRead": true,storage: {publicRead: true}). Existing buckets keep their setting, and anenvoryx.ymlwithoutpublicReadleaves an existing bucket alone. An exported manifest now names a public bucket withpublicRead: true. - Removing Redis, RabbitMQ, Meilisearch, Typesense or OpenSearch keeps
its data volume unless Delete the data is chosen in the dialog: adding
the service again later reuses the data with the credentials it was
created with. The card that adds the service shows kept data with
Delete kept data; kept data is not backed up, moves along with a
rename and is deleted with the project. In the API a removal without
removeDatanow keeps the data instead of answering 422;"removeData": truestill deletes it.GET .../extraslists kept volumes underkeptData,DELETE .../extras/{kind}/datadeletes one. - The API refuses
"exposePort": falsefor Mailpit and Meilisearch with a 422 instead of ignoring it: their port carries the inbox and the dashboard and is always published. - The documentation of the database browser no longer claims that the database password never reaches the browser: the automatic login puts it into a hidden field of the Adminer login page. The page is only served to an Envoryx session with operate access.
Security
- An admin API token could show and replace the secret key that decrypts every stored secret. Like user and token management, both now need an admin’s browser session; tokens are refused, even with admin scope.
- Restoring an instance backup brought back the browser sessions the backup held: a cookie from back then worked again, and a logout or a disabled account since was undone for that session. The restore now ends every session. API tokens are the backup’s and keep working; a token revoked after the backup was taken has to be revoked again.
- The dashboard showed users with access to only some projects the instance-wide count of orphaned resources, the reconcile issues of every project (with its name) and the resource usage of every project. It now lists issues and usage of the caller’s projects only, and the orphan count only for instance admins.
- The database browser opened the database of any project for anyone who
could operate a single one: its credentials file holds the logins of
every project, root included, and
/dbtool/did not check which database a request named. Envoryx now checks every request and opens only databases of projects the user or token may operate; other servers are refused. The Adminer container also logged in anyone who reached it directly, which the containers of every project whose database had been opened in it could do over the shared network; it now answers only requests that come through Envoryx. An existing container is replaced on the next reconcile or open. - The project list, a project and the dashboard handed the service credentials to everyone who could see the project, viewers and read tokens included: the object storage access and secret key, the RabbitMQ password, the Meilisearch and Typesense keys and the password of an external Redis. Like database passwords, they now come only from the credentials endpoints, which need operate. A service kind Envoryx adds later shows no settings in these responses until they are known to be free of secrets.
Fixed
- Several Envoryx instances on one Docker host (a test instance next to
production, two instances on an Unraid server) no longer delete each
other’s containers and networks as orphans. Every instance now has an
ID (
/config/instance-id, created at the first start, kept out of instance backups like the secret key) and labels everything it creates with it (envoryx.instance); what another instance labelled is never listed, stopped, changed or removed, and a project name whose containers, network or volumes the other instance holds is refused instead of shared. Existing containers need no restart: unlabelled ones of known projects stay the instance’s own and get the label when they are next recreated, and the label is not part of the container fingerprint. The network pool still steps around every network on the host. - Only one Envoryx instance per Docker host could use the database
browser: its container and network were both called
envoryx-dbtool, and a second instance got “the name is taken by another Envoryx instance”. They are now named after the instance ID (envoryx-dbtool-plus its first 8 characters). An existingenvoryx-dbtoolof the instance is removed at the start and the next Open database creates the new one; another instance’s is left alone. - The project overview and the Docker page listed each published port twice (“:28001 → 3000” for IPv4 and for IPv6) when ports are published on all interfaces. Each port is listed once now.
- Turning the Node dev server off dropped its preset, script and port, and turning it back on through the API or CLI started a Vite server on a new host port. The settings and the host port are now kept while the dev server is off, and an update that leaves fields empty keeps the stored values.
- Changing a project’s repository URL stored the new URL but left the
checkout’s
originon the old one, so pull kept using the old repository. Saving a new URL now movesoriginalong. - Error messages of an external Redis or database no longer garble words
that contain the password: a password like
WRONGturned Redis’sWRONGPASS invalid username-password pairinto***PASS .... The password is still hidden where it appears on its own, e.g. inredis://:***@host. envoryx project listandproject showprinted the project URL without the proxy’s port (https://shop.testwhere the proxy listens on 18443). Projects in the API now carry their fullurl, and the CLI prints it.- The CLI showed only “an internal error occurred” for server errors and dropped the cause the server sends along; it prints the cause now.
- The single sign-on client secret could not be removed: an empty field
keeps the stored one, also when single sign-on is switched off. The
settings card has Remove the stored client secret when saving, the
API takes
"clearSecret": true. - The Symfony Messenger consumer no longer restarts in a loop until the
messenger_messagestable exists: it runsmessenger:setup-transportsfor its transports first, which creates the table the recipe’sdoctrine://default?auto_setup=0leaves to the application, and waits with a hint while the database cannot be reached. Existing Messenger workers are recreated once on the next start. - Imported Symfony sites no longer fail with
Invalid platform version: with “Adapt the configuration” on,config/packages/doctrine.yamlreadsserver_versionfromDB_SERVER_VERSION, as in the Symfony template, unless it sets a version itself. The site’s.envusually names it inDATABASE_URL, which the injected URL replaces without one. - Ruby containers get
CACHE_DATABASE_URLandCABLE_DATABASE_URL(<database>_cache,<database>_cable) next toQUEUE_DATABASE_URL, so the Solid Cache and Solid Cable entries of a Rails 8 productiondatabase.ymlreach the project’s database server. Envoryx creates those databases whendatabase.ymlhas the entries, like the queue database. Ruby containers report an older setup once after the update; restart the project when it suits you. - The Ruby images ship libvips, which Active Storage’s default
:vipsprocessor needs for image variants (the images grow by about 180 MB). rails testruns in the Tests section show their counts and failed tests (name, message, file and line), read from the minitest output; before, only the exit code counted.- A new cron job ran once right away, in the minute it was created, instead of first at its next scheduled time. Only jobs due in the minute Envoryx starts still run in that minute.
- A user whose global role is none could not see the schedule preview in a project’s cron editor (403). The Database tab no longer asks viewers for the database browser’s state, which they may not read.
- Asking for a token scope above your role answered “scope must be read, operate or admin: your role allows at most read tokens”, as if the scope name were wrong. It now reads “your role allows at most read tokens”.
- A read token confined to one project that called an operate tool over
MCP (e.g.
stop_project) was told “limited to particular projects” instead of “this token has read scope, the operation needs operate”, as the REST API says. The same applied to the database browser routes. - The Go debugging texts (IDE tab, Go server settings, deployment guide) said breakpoints survive every rebuild. When air rebuilds, it restarts Delve and the IDE’s debug session ends; the texts now say to attach again, after which the breakpoints are hit as before.
- The diagnostics check “Wildcard DNS as seen by Envoryx” reported OK for any answer when no host for project links was set, even when the names pointed at another machine. It now asks that address for the probe host and only reports OK when this instance’s proxy answers; another server there is a warning, no answer is reported as unverified.
PATCH /projects/{id}with a service object that leaves outenabled(e.g.{"memcached": {"exposePort": false}}) removed the service; a missingenabledorexposePortnow keeps what the service has. A version change in the Services section no longer sends the port along.- The certificates the local CA issued for a project’s host names stayed
in
/config/ca/certsafter the project was deleted and went into every instance backup. Deleting a project now removes the certificates of host names nothing routes to any more, and the first start after the update clears those left behind by earlier deletions.
0.17.0 - 2026-09-30
Fixes from a full system test, four of them for security (see
Security). After the update the dashboard reports every running
container once as running with an older setup, because the container
fingerprint now includes the environment; restart each project when it
suits you. Ruby projects install their bundle once more on that restart,
and PHP projects pull the new PHP image. New project networks come from
ENVORYX_NETWORK_POOL (10.213.0.0/16); set another range if that one is
used in your network.
Added
- Envoryx sets
DB_SERVER_VERSIONfor the MariaDB, MySQL and PostgreSQL servers it runs, in the form Doctrine expects (18,8.4.0,mariadb-11.4.0); additional servers get it with their prefix. - A MongoDB 8.0 database can move to 8.2 in place, which MongoDB supports; 8.2 takes over the data as it is (the feature compatibility version stays at 8.0). On Docker hosts where the old version cannot start, the backup taken first is a copy of the data volume instead of a dump; restore, download and offsite copies handle it.
- PHP’s
mail()reaches Mailpit: the PHP images ship msmtp, and while Mailpit is on,sendmail_pathsends through it. Without Mailpitmail()returns false as before.
Changed
- New project networks get a /24 out of
ENVORYX_NETWORK_POOL(10.213.0.0/16unless set, 256 projects) instead of one of Docker’s default ranges, which ran out after about 30 projects: every project keeps its network while stopped, and creating the next one then failed with HTTP 500. Existing networks keep their range. If the range is used in your LAN or VPN, set another one;offkeeps Docker’s choice, and its running out now says what to do. - A container’s environment variables now count when Envoryx decides whether it is up to date. Workers stayed in development after a switch to production mode (Ruby), and containers kept newer variables after an instance restore; a restart now recreates them. After this update the dashboard reports every running container once as running with an older setup; restart each project when it suits you.
GEM_HOMEin Ruby containers now names the Ruby version (~/.gem/ruby/3.4.0), the directory RubyGems uses for user installs. RubyMine’s debugger gems are found without aGEM_PATHworkaround, and gems built for one Ruby version no longer meet another. Existing Ruby projects install their bundle once more after the restart; the old contents of~/.gem/rubyin a project home can be deleted.- Runtime and worker containers always resolve
host.docker.internalto the Docker host, not only with an external service. Xdebug’s default and PyCharm’s debug server work when the IDE runs on the Docker host; the IDE section explainsssh -Rfor an IDE on another machine. - The interface leaves out what your role may not do in a project:
viewers see no start, restore or delete buttons, and forms they may
only read are shown read-only.
/dockerand/projects/newsend non-admins to the project list. Whether you are an admin now comes from the server, so a session with a limited API token shows the right controls. - The Rails template gives Solid Queue its own database in development,
as Rails does in production:
database.ymlgets aqueueentry, Envoryx creates<database>_queueand setsQUEUE_DATABASE_URL, which also reaches the productionqueueentry. A Solid Queue worker whose tables are missing waits with a hint instead of restarting in a loop. - PHP 8.6 (preview): extensions that don’t build for it yet (redis, imagick, memcached, amqp, mongodb, Xdebug) are greyed out and refused instead of offered and silently missing. mbstring, which the upstream 8.6 image no longer compiles in, is installed in every PHP image.
- MongoDB 8.0 and 7.0 cannot be chosen on Docker hosts with Linux 6.19 or newer, where they don’t start. Existing projects with such a database get a hint (8.0: upgrade to 8.2; 7.0 has no way to 8.2 on such a host) and start without that database instead of restarting it in a loop.
Security
-
With JetBrains Gateway on, every project mounted the whole shared
~/.cache/JetBrains. Besides the downloaded IDE backends it holds index caches with the source, local history and join links with their tokens, so one project could read another project’s source and join links, in its container and over SFTP. OnlyRemoteDev/distwith the backends is shared now; the rest stays in each project home. The old shared data in/config/jetbrainsis no longer mounted; everything exceptRemoteDev/distthere can be deleted. -
Adding an IP allowlist to a project that was shared publicly was accepted, but the tunnel reaches the proxy from Envoryx itself, so the share stayed public (and an allowlist naming 127.0.0.1 let the internet in). An allowlist is now refused while a share runs.
-
The resource comparison (
GET /metrics/overview, the dashboard’s resource card) listed every project’s name and usage to users who have a role in only some projects; it now shows only theirs. -
Viewers and read-only API tokens received the values of secret environment variables in the project list, the project view and the dashboard. Below operate these values are now left out.
Fixed
- The diagnostics check “Project domains from this browser” could never pass: the page’s own content security policy blocked the request, and the check then blamed DNS. The policy now lets that one probe host through.
- A failed create left the files of a template, clone or starter page in the project directory, and trying again with the same name failed with “templates need an empty directory”. The rollback now removes a directory the create made and empties one it found empty; a directory that already held files is still never touched.
- MongoDB snapshots, project backups, clones and branch copies failed with HTTP 500: mongodump 100.17 and later refuse the connection string Envoryx passed.
- The periodic reconcile could set a project whose create had just finished to failed (“creating was interrupted by an Envoryx restart”).
- Behind HTTPS, PHP projects on Caddy saw
X-Forwarded-Proto: http, so TYPO3 answered 404 and Shopware “Sales Channel Not Found” after setup. Caddy also served dotfiles such as.envand.htaccessin PHP projects; like nginx and Apache it now answers 404. - Next.js failed to build in production mode (it ran under
NODE_ENV=development), and Nuxt’s production mode started astartscript its starter doesn’t have; it now usespreview. - The pip install action failed after a Python version change (“No
module named pip”); it now rebuilds the stale
.venv. - An error from a failed start with one custom image stayed on the project after a later image started fine.
- Rider’s “Attach to Remote Process” over SSH failed (“cancel-tcpip-forward failed”): a forward asked for port 0 couldn’t be cancelled.
- The IDE section gave DataGrip a MongoDB URL with the user in it, so the password was ignored; the Node inspector example named the wrong script; the Gateway steps named WebStorm for every project without PHP; the Ruby text didn’t say RubyMine’s debugger needs Allow JetBrains Gateway.
gradle testoutput in the Tests section ended in console control sequences.- Backup downloads, offsite copies and archive imports left out the archives of addon volumes, so a backup restored from a download or from offsite brought back no addon data.
- Symfony’s Doctrine migrations failed with “Invalid platform version”
because the injected
DATABASE_URLcarries no server version; the template now readsDB_SERVER_VERSIONindoctrine.yaml. Existing Symfony projects can addserver_version: '%env(DB_SERVER_VERSION)%'underdoctrine.dbalthemselves. - A project’s stats took up to 18 s with many containers on the host, because every container on the host was sampled. Only the project’s containers are sampled now.
- On a phone the project’s database card was wider than the screen.
0.16.2 - 2026-09-29
Added
- The dashboard notes containers that an older Envoryx created from another
spec (a changed command or health check) and asks for a restart of the
project, which recreates them. Such containers kept running as they were;
a PostgreSQL container from before 0.10.0, for example, kept logging
FATAL: role "root" does not existevery ten seconds.
Fixed
- A project whose name starts with “pg” and a separator (e.g. “PG Probe”)
got the database user
pg_…, which PostgreSQL refuses, so the database never started. Such names, the system databases (postgres,mysql,admin…) androotnow get anapp_prefix. Existing projects keep their names.
0.16.1 - 2026-09-29
Changed
- Pages use the full width of the window. Settings → Profile → Appearance switches back to the centred column (Boxed); like the accent colour, the choice is stored per browser.
Fixed
- The status warnings of a project (“project should be running but is missing”, the notes on images that a restart applies and on containers of removed workers or services) showed in English in every language.
0.16.0 - 2026-09-29
The interface has a new layout. Projects, the settings and the Docker page use a grouped sidebar instead of long rows of tabs, and the new-project wizard starts with where a project comes from. Several settings moved (see Changed); links to the old places still land on the right one.
Added
- New-project wizard in three steps. It starts from a gallery of all templates, with a search and a runtime filter, or from a Git repository, an existing website or an empty project; the runtime follows from that choice, and a repository names the project after itself. The second step holds the name, the database and the services; runtime versions, more runtimes as tools, the web server, the directories and the environment wait under Advanced settings. A summary next to every step shows each choice and jumps to where it’s set.
- Python 3.15 as a preview version.
Changed
- A project’s sections sit in a sidebar: Overview, then Develop
(Terminal, Actions, Tests, IDE), Code (Git, Branches), Configuration
(Runtime, Environment, Domains, Workers & cron), Data (Database,
Services, Backups) and Observe (Logs, Resources, History), with a select
on phones. The chosen section is kept in the address (
?tab=). Workers and cron jobs share one section. Share publicly, Rename, Duplicate, Delete and the Docker plan (formerly the Advanced tab) moved into the More actions menu in the header; a Shared badge stays visible while a project is shared. - The settings use the same sidebar. Everyone has My account (Profile with appearance and password, API tokens & MCP, SSH keys). Admins also get Instance (Diagnostics, General, Domains & HTTPS), Access & security (Users, SSH access, Git deploy key, Secret key), Operations (Backups, Notifications, Retention, Audit log) and Extensions (Addons, Database browser, Package cache, Private registries). The host for project links and the Xdebug host moved to Domains & HTTPS, log, resource and audit log retention to Retention, and the secret key from Access to Settings → Secret key.
- The dashboard shows one box of notices (set-up check, update, inconsistencies, what Envoryx did on its own), the recent projects with start, stop and open, and a system card with Docker, CPU, memory and disk space. Users who aren’t admins no longer get links to the Docker page.
- The Docker page is split into Overview, Containers (grouped by project), Networks & volumes (with the project each one belongs to) and Images. Host paths are checked in the diagnostics.
- Your username at the bottom of the navigation opens your profile. The German interface calls the dashboard “Dashboard”.
- Python 3.14 is the default for new projects.
- The Envoryx image is built on Alpine 3.24.
Fixed
- Dialogs could announce the title of another dialog to screen readers.
- On phones, project rows squeezed the project name to one letter, and the dashboard scrolled sideways.
0.15.0 - 2026-09-29
Keep a copy of your secret key. This version encrypts the stored secrets
at its first start. Afterwards, open Settings → Access → Secret key and
store the key somewhere safe, or set it as ENVORYX_SECRET_KEY (the Unraid
template has an optional field for it). Without the key, an instance backup
can’t be restored on another host.
Added
- Secrets at rest are encrypted (AES-256-GCM): Git tokens, secret project
variables, service credentials, addon secrets, the single sign-on client
secret and registry logins in the database, the notification, offsite and
Let’s Encrypt settings files, and the project export in every project
backup. The key comes from
ENVORYX_SECRET_KEYor, without it, from/config/secret.key, which Envoryx creates. Settings → Access → Secret key shows the key (keep a copy) and replaces it; with the variable,ENVORYX_SECRET_KEY_OLDcarries the previous key for one start. Instance backups never contain the key and record its ID; restoring one made with another key asks for that key. A start whose key doesn’t fit the database is refused with the key ID it needs.
Changed
- The first start of this version encrypts the existing secrets, files and
project backups in place and, unless
ENVORYX_SECRET_KEYis set, creates/config/secret.key. Keep a copy of the key: without it an instance backup can’t be restored on another host. Instance backups made with earlier versions still hold the secrets unencrypted.
0.14.0 - 2026-09-29
Added
- Custom runtime images. A runtime (PHP, Node.js, Python, Go, Ruby, Java,
.NET) can run an image from a registry or one Envoryx builds from a
Dockerfile in the project (Runtime → Runtime images). The Dockerfile’s
directory is the build context; when a file there changes, the next start
builds a new image, and Rebuild without cache fetches fresh base images.
Workers and cron jobs follow their runtime; databases, services and web
servers keep the vetted images. Every image is checked when it’s set or
built, and what it lacks (a shell, git, socat, the runtime’s tools, Xdebug
…) is shown as a warning, but the image is used anyway.
envoryx.ymltakesimage:ordockerfile:per runtime. - Private registry logins under Settings → Tools → Private registries,
used for pulls and for the
FROMimages of builds. Passwords are write-only. - Built images (
envoryx-build/…) are listed and removed with the other unused images. - Addons: services described in a YAML file instead of Envoryx’s code. An
addon is one container per project with selectable image versions,
environment (with secrets generated per project), named volumes, a port
with an optional web UI at
<slug>-<name>.<base domain>and a host port, a health check, the variables it injects into the application and the credentials the UI shows. Admins install files under Settings → Addons (write one, paste one, install from a URL, or start from the shipped pgAdmin, phpMyAdmin, Elasticsearch, Soketi and Keycloak examples); developers add installed addons on a project’s Services tab. The format has no way to ask for privileges, host paths, the host network or devices. A new version of a file reaches the projects using it at their next start. Addon volumes go into database backups (the container stops while they are read) and come back on restore.envoryx.ymltakesaddons:.
0.13.0 - 2026-09-28
Added
- Java runtime. The wizard’s first step offers Java application; a Java
container (
ghcr.io/envoryx/envoryx-java:<17|21|25>, Eclipse Temurin plus Maven and Gradle, LTS releases only) is added on any project from the Runtime tab, too. Run the Java server runs Spring Boot (spring-boot:runorbootRun, DevTools restarts on compiled changes), Quarkus (quarkus:devorquarkusDev, live reload) or a plain jar; production mode builds once and runs the jar. Maven or Gradle follows the project’s build file, and itsmvnw/gradlewwins. Without PHP and without a Python, Go or Ruby server the project URL reaches it. Debug with JDWP publishes a JDWP port for IntelliJ IDEA’s Remote JVM Debug and VS Code (the IDE tab has both). Maven’s repository and Gradle’s caches are shared by all projects. SPRING_DATASOURCE_*,QUARKUS_DATASOURCE_*andJDBC_URL(plus<NAME>_JDBC_URLper additional database) point Java apps at the project database; MongoDB, Redis and Mailpit get the Spring and Quarkus variables, too. Quarkus Dev Services are switched off.- Java templates Spring Boot (start.spring.io) and Quarkus REST
(code.quarkus.io), with JPA or Hibernate and the driver of the project
database; Maven and Gradle actions;
mvn test/gradle teston the Tests tab against<database>_test, with results per test; Jar file and Build tool goal workers; cron jobs, SSH (<project>.java), the terminal, logs, the manifest (java:), the CLI (--java,--java-server,--java-preset) and the MCP tools know Java. - Site import recognises
pom.xmlandbuild.gradle. - .NET runtime. The wizard’s first step offers .NET application; a .NET
container (
ghcr.io/envoryx/envoryx-dotnet:<8|10>, the official SDK image plusdotnet-efand the netcoredbg debugger, LTS releases only) is added on any project from the Runtime tab, too. Run the .NET server runs ASP.NET Core underdotnet watch(hot reload, restart when an edit can’t be applied) or publishes once and runs the DLL in production mode; the DLL preset publishes and runs any other application (worker services, console hosts). The project file is found on its own (the one at the top, else the one web or worker project) or set by hand. Without PHP and without a Python, Go, Ruby or Java server the project URL reaches it. NuGet packages are shared by all projects. ConnectionStrings__DefaultConnection(ADO.NET form for Npgsql and the MySQL providers) points .NET apps at the primary SQL database,ConnectionStrings__<name>at each additional one, andConnectionStrings__MongoDBandConnectionStrings__Redisat those services.- Debugging .NET needs no port: VS Code starts
netcoredbgin the container throughpipeTransportover the SSH user<project>.dotnet, Rider and Visual Studio attach to the remote process over SSH. - .NET templates ASP.NET Core Web API (EF Core with a sample
/todosendpoint on PostgreSQL, MySQL or MariaDB), ASP.NET Core MVC, Blazor Web App and ASP.NET Core Razor Pages, all fromdotnet new;dotnetanddotnet efactions;dotnet teston the Tests tab against<database>_test, with results per test from the TRX report; Project and DLL workers; cron jobs, SSH (<project>.dotnet), the terminal, logs, the manifest (dotnet:), the CLI (--dotnet,--dotnet-server,--dotnet-preset) and the MCP tools know .NET. - Site import recognises solution and project files (
.sln,.slnx,.csproj,.fsproj,.vbproj). - Branch environments. The new Branches tab makes a copy of a project on
another branch of its repository: files (with
.env,vendor/andnode_modules/), database, bucket, workers and cron jobs are copied, the working tree switches to the branch, and the copy starts under its own URL (<project>-<branch>.test). Deploy commands set on the parent (saycomposer installandphp artisan migrate --force) run after the copy and after every pull, in the environment’s application container. Watch the repository asks the remote withgit ls-remote, so it works behind NAT without a webhook: a push is pulled and deployed, a deleted branch takes its environment with it, and a new branch matching a pattern likefeature/*gets one (up to five by default). Environments nobody opened for a set number of days are stopped. A project with environments can’t be deleted until they’re gone. branches:inenvoryx.yml,envoryx project branches|branch|deploy, the MCP toolslist_branch_environments,create_branch_environmentanddeploy_branch_environment, and the notificationsbranch.failed(on by default) andbranch.changed(off by default).- Users and roles. Settings → Users invites people: you pick a name and a role, Envoryx hands you a link that works once within 48 hours (it sends no mail), and the new user sets a password through it. A new link resets a forgotten password. The roles match the token scopes: Viewer looks, Developer works with projects (start, stop, terminal, actions, git, backups), Admin does everything including users and settings, and No access reaches only the projects a user is given. A role per project replaces the global one there, so someone can be a viewer everywhere and a developer in one shop. A project a user has no access to doesn’t show up anywhere (lists, dashboard, MCP, SSH); branch environments take over their parent’s project roles. Users can be disabled, and the last admin can’t be demoted, disabled or deleted.
- Single sign-on with OpenID Connect (Authentik, Keycloak, Authelia, Google, Dex …): authorization code with PKCE, a Sign in with … button on the login page, and optionally the role set from the provider’s groups at every sign-in. Users can be created at their first sign-in, or only invited ones get in, and an existing account is never taken over by its name.
- Every user has their own SSH keys (My SSH keys), which act with that user’s roles, and manages their own API tokens.
Changed
- An API token never does more than the user it belongs to, whatever its scope; a scope above the owner’s role is refused when the token is created. Admins see everyone’s tokens with their owner, everyone else only their own. Existing accounts stay admins, so existing tokens keep working as before.
- The public keys under Settings → Access → SSH access are now the Admin keys: they still open every project. Keys that should follow a user’s roles go under that user’s My SSH keys.
- README, DEPLOYMENT, ARCHITECTURE, DEVELOPMENT, SECURITY and CONTRIBUTING are rewritten in plainer words, and every code comment with them.
0.12.0 - 2026-09-27
Added
- Go runtime. The wizard’s first step offers Go application next to PHP,
Python, Node.js and static; a Go container
(
ghcr.io/envoryx/envoryx-go:<1.26-1.27>, officialgolang:<v>-bookwormimage plus air, Delve and gotestsum) is added on any project from the Runtime tab, too. Build and run the server builds the main package (.or e.g../cmd/server) and runs it as the container’s main process - rebuilt by air on every change in development mode (a project’s.air.tomlwins), built once in production mode - on$PORT(8080). Without PHP and without a Python server the project URL reaches it; before ago.modexists the container waits instead of crash-looping. Debug with Delve runs the server under a headless Delve on a published port for GoLand and VS Code (the IDE tab has both configurations); without the server the port serves adlvstarted in the terminal. The module and build caches are shared by all projects (and listed under Settings → Package cache). - Go templates Go (net/http), Gin and Echo; Go actions (
go version,go build,go vet,gofmt -l,go mod tidy/download,go generate); the Tests tab runsgo test ./...through gotestsum; worker preset Go program (builds a package, runs the binary); cron jobs, SSH (<project>.go), the site import (go.mod),envoryx.yml(go:), the API (go), MCP (goVersion,goServer,goPackage,goPort,goMode) and the CLI (--go,--go-server,--go-package) cover Go. The image builds and the weekly runtime-version check include Go. - Ruby runtime. The wizard’s first step offers Ruby application; a Ruby
container (
ghcr.io/envoryx/envoryx-ruby:<3.3-4.0>, officialruby:<v>-slim-bookwormimage plus the build dependencies of common gems and the debug gem, no Node.js) is added on any project from the Runtime tab, too. Run the Ruby server has two presets: Rails (bin/rails serverin development mode, Puma in production mode) and Rack (Puma onconfig.ru- Sinatra, Roda, Hanami …), on$PORT(3000/9292). Before it starts, the server waits for itsGemfileand runsbundle installwhen the bundle is incomplete, so a cloned application comes up on its own; the gems live in the project home, Bundler’s download cache is shared by all projects.RAILS_ENV,RACK_ENV,APP_ENVandHANAMI_ENVfollow the mode, and Rails in development accepts the project’s host names. Without PHP and without a Python or Go server the project URL reaches it. Debug with rdbg runs the server underrdbg --openon a published port for VS Code’s rdbg extension orrdbg -A(the IDE tab has the configuration); without the server the port serves anrdbgstarted in the terminal. RubyMine debugs through its SSH remote interpreter (<project>.ruby) with its own debugger - its Ruby remote debug speaks onlyruby-debug-ide. The Ruby containers get PostgreSQL’sDATABASE_URLaspostgresql://, which Active Record understands. - Ruby templates Rails (Hotwire with importmap), Rails (API only) and
Sinatra; Bundler and Rails actions (
bundle install/update/outdated,rubocop,rails db:prepare/migrate/rollback/seed,routes,assets:precompile,tmp:clear,about); the Tests tab runsrspec(with a JUnit report whenrspec_junit_formatteris in the bundle) andrails testagainst<database>_test, which Envoryx creates - never against the development database; worker presets Solid Queue, GoodJob, Sidekiq, Rake task and Ruby script, running in the server’s environment; cron jobs, SSH (<project>.ruby), the site import (Gemfile),envoryx.yml(ruby:), the API (ruby), MCP (rubyVersion,rubyServer,rubyPreset,rubyPort,rubyMode) and the CLI (--ruby,--ruby-server,--ruby-preset) cover Ruby. The image builds and the weekly runtime-version check include Ruby.
Fixed
- SSH sessions into the Python container (
<project>.python, also IDEs that probe over SSH) now find the project’s.venvfirst onPATH-python,pip,pytestresolved to the image’s interpreter, because the login shell resetPATH. Needs the updatedenvoryx-pythonimages.
0.11.0 - 2026-09-26
Added
- External databases and Redis: a database (primary or additional) or Redis
can be an existing MariaDB, MySQL, PostgreSQL or Redis server instead of a
container of the project - On an external server in the wizard, the
Database tab and the Services tab,
externalin the API and inenvoryx.yml(without the password). The connection is tested before it is stored (and with Test connection beforehand);host.docker.internalreaches a server on the Docker host. Backups, snapshots, restores, cloning, the site import and Adminer work against the server; Envoryx never drops databases there, never changes its password and only forgets the connection when it is removed. A duplicate gets local containers with the data. - Ollama as a project service for local LLMs (Services, the wizard,
--ollama, MCP,ollama:inenvoryx.yml). The application getsOLLAMA_HOST,OLLAMA_BASE_URLandOLLAMA_URL. All projects share one model store, so a model is downloaded once; models are downloaded, followed, cancelled and deleted on the Ollama card. Use the GPU hands the host’s NVIDIA GPUs to Ollama; Envoryx checks beforehand that Docker can (NVIDIA Container Toolkit, on Unraid the Nvidia Driver plugin) and says so when it cannot. - The audit log can be searched and filtered by user (with its API tokens), category, project and date, pages back through older entries, and exports the filtered entries as CSV or JSON Lines. Every project has its own History tab. A project change now records each setting it changed with its value before and after, shown when the entry is opened. A retention (30 days to two years, or forever as before) is set under Settings → Audit log.
Changed
- Builds of the development branch (
:main) are named after the release they follow:0.10.0+5 (73304d6)- five commits after 0.10.0 - instead ofmain-and the full commit hash. Releases show their number without thev.
0.10.0 - 2026-09-26
Added
ENVORYX_URL: the address a project answers at, injected into its containers and recomputed with every plan (after a rename, too) - forAPP_URL=${ENVORYX_URL}in a.env.- Rules for a project’s host names, applied by the proxy (Domains → Rules,
PUT /projects/{id}/proxy-rules): an address allowlist, HTTP basic authentication, redirects (paths and prefixes, to paths or other hosts), response headers to set or remove, and CORS with preflight answers. They also apply to a share, which now goes through the proxy - a password protects the public address. - Share a project on a temporary public https address: a Cloudflare quick
tunnel (
*.trycloudflare.com, no account, no port forwarding) started from the project header,envoryx project shareor the API, for 5 minutes to 24 hours. It ends when its time is up, when the project stops, or by hand. - Templates for Drupal (with Drush), TYPO3, Shopware and Craft CMS. Each is
created with
composer create-projectand wired to the project database: Drupal’ssettings.php, TYPO3’sconfig/system/additional.php, Craft’s.envand Shopware’sAPP_URLread the variables Envoryx injects. Their installers need the running database, so they are actions: drush site:install, typo3 setup and craft install create the administrator and print a generated password, system:install sets Shopware up (admin / shopware). The web installers of Drupal, TYPO3 and Craft work too.
Fixed
- pnpm failed in the Node container with
EACCESunder/usr/local/lib/corepack: the image’s pnpm lacked its native binary and fetched it on first use, and a version pinned inpackageManagercould not be downloaded either, as the containers run as the project user. The Node images now carry the binary and let Corepack add versions for any user. - PostgreSQL no longer logs
FATAL: role "root" does not existevery ten seconds: the health check logs in as the project’s database user, andpsqlin the container’s terminal now does too. - A project with PHP and a PostgreSQL database got no
pdo_pgsqlextension, so the Symfony template (PostgreSQL by default) and every PostgreSQL application found no driver. It is now switched on with the database, also when one is added later. - Deleting a project with its files failed when an application had made a
directory read-only (Drupal locks
web/sites/default) and Envoryx does not run as root. - Template steps run with the project’s
php.ini, so the extensions an application’scomposer.jsonasks for (gd,intl…) are available duringcomposer create-project.
0.9.0 - 2026-09-26
Added
.envimport and export for a project’s variables (Environment tab and the wizard): Import .env reads a pasted or chosen file (export, comments, quotes,${VAR}references), selects new and changed variables, leaves out the ones Envoryx sets for the project’s services (soDB_HOST=127.0.0.1of the old setup does not win over the project database), refuses reserved names and multi-line values, and marks secrets by name or by a password in a URL. Export .env downloads the variables as a file.- Import an existing website: New project → Start from: Existing website
takes a ZIP or tar.gz of a site’s files and optionally a database dump
(
.sql,.sql.gz). Envoryx recognises WordPress, Laravel, Symfony, Drupal, TYPO3, Joomla, Shopware, Craft CMS and plain PHP, static, Node.js and Python sites and fills the wizard with PHP version, extensions, document root, web server (Apache when the site relies on.htaccess) and database. Optionally the site’s configuration is wired to the project database -wp-config.php, Drupal’ssettings.php, TYPO3’s additional configuration, Joomla’sconfiguration.php; each original is kept as*.envoryx-original.phpthat answers 404 - and the old server’s configuration caches are removed. The dump is imported without the statements that tie it to the old server (USE,CREATE DATABASE, owners and grants); a failed import rolls the project back.envoryx import <folder|archive> [name] --db dump.sqldoes the same from the command line and packs a local folder on the fly. See DEPLOYMENT.md, Importing an existing website. - Several databases per project. Next to the primary database (host
database,DB_*) a project can have any number of additional ones with a name of their own - for example PostgreSQLanalyticsnext to MariaDB: its own container (envoryx-<project>-db-analytics), volume and credentials, reached as hostanalytics, injectingANALYTICS_DB_*andANALYTICS_DATABASE_URL. The wizard adds them under Additional databases, the Database tab switches between them and adds or removes one; version, published port, password rotation, Adminer, snapshots (per database) and cloning (from any database of the same engine, of another project or the same one) work for each. Backups dump every database (database-<name>.sql.gznext todatabase.sql.gz), duplicating and renaming carry them along.envoryx.ymllists them underdatabases:; the CLI hasproject create --add-database NAME=TYPE[:VERSION]and--db NAMEon everydbcommand; the API takes?db=<name>on the database routes, lists them atGET /projects/{id}/databasesand changes them withPATCHand"databases"; MCP tools takedbandadditionalDatabases. - Shared package cache: Composer, npm, Yarn, pip and uv keep their downloads in
/config/cache, which the application containers, the workers and the template scaffolds of every project share, so a package is downloaded once - a second Laravel project is created in about a quarter of the time. Settings → Tools → Package cache shows its size per tool and empties it. Instance backups leave it out. - Test runner (Tests tab): Envoryx finds a project’s test suites - Pest,
PHPUnit (also Symfony’s
bin/phpunit), thetest/test:*scripts ofpackage.json, Playwright, Cypress, pytest and Django - and runs them in the runtime container with live output and an optional filter. Where the runner writes a JUnit report, the result lists every failed test with its message, file and line; the last 50 runs of a project are kept.
Fixed
- Renaming a project whose database is PostgreSQL failed with “session user cannot be renamed”: Envoryx logged in as the very login it renamed, the only superuser there is. The rename now runs through a short-lived helper login that is removed right afterwards.
0.8.0 - 2026-09-25
Added
- Application health checks (Overview tab): a path such as
/healthmust answer with the expected status. Envoryx asks the web server the way the proxy does (project network, the project’s host name), every 30 s by default; after 3 failures in a row the application counts as down, the project shows a warning and a notification goes out (new eventproject.down, on by default), and another one when it answers again. Checks pause while a project is stopped, busy or its application container is not running. Test tries a check before saving it. Part ofenvoryx.yml(healthcheck:) andenvoryx project show. - New notification events (
project.oom,project.down) are on after the update also where the event selection was saved before: an event type the selection did not offer yet follows its default until it is saved again. - Resource history: every running project container is sampled once a minute (CPU, memory, network, disk I/O) and each project’s disk space - volumes, project directory, backups - once an hour. The new Resources tab of a project charts it from one hour to one year (with a table view per chart), and the dashboard lists every project’s average and peak usage, busiest first. Values are kept in full for a day, as 5-minute averages for a week and hourly after that; Settings → General → Resource history sets the retention (default 90 days, up to a year) and deletes the history.
- Resource limits per project (Resources tab): CPU cores and memory for the
application containers (web server, PHP, Node.js, Python, workers) and,
separately, for the services (database, caches, search, storage), each per
container, plus a process limit that every container now has (default 4096)
against fork bombs and runaway worker pools. Changes reach running
containers at once; only lifting a limit recreates a container. The card
shows each container’s CPU and memory against its limit. When a container
runs out of memory - also when only a child process is killed and the
container keeps running - the project shows a warning for a day and a
notification goes out (new event
project.oom, on by default). Limits are part ofenvoryx.yml(limits:) and ofenvoryx project show.
0.7.1 - 2026-09-24
Fixed
- Recreating a running container - after a version or port change, a rename -
killed it outright. A database, Redis or RabbitMQ lost what it had not
written yet: Redis everything since its last snapshot, MongoDB the latest
writes. Envoryx now stops the container first, as
docker stopwould, and gives it its stop timeout to shut down cleanly. - Database commands right after a database’s very first start could fail with
“terminating connection due to administrator command”. The images
initialise through a temporary server that listens on the unix socket only
and is shut down right afterwards; Envoryx’s clients used that socket.
psql,pg_dump,mysql,mysqldump,mariadbandmariadb-dumpnow connect over TCP to 127.0.0.1, which only the real server answers (MongoDB did already).
0.7.0 - 2026-09-24
Added
- Project manifest
envoryx.yml: runtimes (with PHP extensions and ini settings), web server, database, services, domains, environment (secrets by name only), workers and cron jobs as a file in the repository.envoryx upin a clone creates the project from it - the server clones the repository’s origin at the checked-out branch - or brings an existing project in line;--dry-runshows the changes, removals need--prune, missing secret values are asked for.envoryx project manifest <project>writes the file for an existing project. The Git tab shows the project asenvoryx.yml, saves it into the project directory and applies a changed file after a pull; the wizard applies the manifest of a repository it clones. API:…/manifest,…/manifest/file,…/manifest/plan,…/manifest/applyandPOST /projects/from-manifest. - Log history in the Logs tab: pick a time range (last 15 minutes to 30 days,
everything, or from/to), search and filter to warnings or errors on the
server, see the error frequency as a chart (click a bar to zoom into that
slot) and the most frequent errors and warnings grouped with numbers, ids and
times masked. Download now saves every matching line instead of the last
10,000. Envoryx guesses the level from the text (PHP, nginx, Caddy,
PostgreSQL, MySQL, Python tracebacks, JSON and logfmt
levelfields, 5xx in access logs) and colours live lines by it. The API takessince,until,q,levelandstreamon…/logsand the live stream, plus…/logs/statsand…/logs/download; the CLI getsenvoryx project logs --since/--until/--grep/--leveland-o FILE, MCPget_logsthe same filters and a newget_log_statstool. - Persistent log history: Envoryx copies the output of every project
container into daily files under
/config/logs, so the History view still finds it after a container was restarted or recreated (image or version change, rename, new password …) and when Docker’s 30 MB per container are long rotated away. Output written while Envoryx was down is picked up afterwards, nothing is stored twice. Finished days are gzipped; retention (7 days) and a size limit for all projects (1 GB) are set in Settings → General → Log history, which also shows the space used and can delete the history. Deleting a project deletes its history; instance backups leave it out. - Offsite backups: Settings → Backups takes targets - S3-compatible storage
(AWS, Backblaze B2, Wasabi, Hetzner Object Storage, Cloudflare R2, MinIO),
SFTP (Hetzner Storage Box, NAS; the server key is pinned) or WebDAV
(Nextcloud, ownCloud) - with a connection test. Scheduled project backups go
up by themselves, a daily instance backup at a chosen hour too, everything
else with Copy offsite or Also copy offsite; each target keeps its own
number of scheduled copies. Archives are optionally encrypted with age
(scrypt passphrase) before they leave the host. Failed uploads are retried
with growing pauses and reported through the Backup failed notification.
The Backups tab shows each copy’s state and lists what a target holds for the
project, deleted backups included; Fetch brings one back. A fresh Envoryx
fetches its instance backup from the target the same way - disaster
recovery in four steps (DEPLOYMENT.md → Offsite backups). CLI:
envoryx backup create --offsite,backup offsite,backup remote,backup fetch. - More DNS providers for the Let’s Encrypt wildcard certificate: Hetzner (through the Hetzner Cloud API - the old DNS Console API was shut down in May 2026), netcup, Amazon Route 53, DigitalOcean and Porkbun, next to Cloudflare. The settings ask for each provider’s own credentials (netcup: customer number, API key and password; Route 53: access key and secret, optionally the hosted zone) and keep the secret ones out of every answer. A stored Cloudflare token is taken over as it is.
Fixed
envoryx project create --database postgres(as the help text suggests) was refused by the server, which only knowspostgresql. The CLI now mapspostgres,pgandpgsqlto it, also in--from-json.- Downloading a project backup left out the object storage archive
(
storage.tar.gz); the download now carries everything the backup holds. - Starting a project whose directory was missing - on a fresh host after a recovery, or removed by hand - let Docker create it for the bind mount, owned by root, so the application could not write to it. Envoryx now creates it as the project user first.
- The certificate check waited for the challenge record at 1.1.1.1; asking before the record existed could get the “does not exist” cached for as long as the zone allows. Envoryx now asks the zone’s own name servers and waits until all of them serve the record.
0.6.0 - 2026-09-24
Added
- Python runtime. The wizard’s first step offers Python application next
to PHP, Node.js and static; a Python container
(
ghcr.io/envoryx/envoryx-python:<3.10-3.14>, official slim image plus pip, uv, git and the build dependencies psycopg/mysqlclient/Pillow need) runs as the project owner with the project’s.venvfirst onPATH. Run the application server makes the preset’s command the container’s main process: Django (manage.py runserver, gunicorn in production mode), Flask (flask run --debug/ gunicorn), FastAPI and any ASGI app (uvicorn,--reloadin dev mode), WSGI (gunicorn) orpython -m <module>. Production mode also turns the frameworks’ debuggers off (DJANGO_DEBUG,FLASK_DEBUG); the Django template ties itsDEBUGto the first. Without PHP the project URL and extra domains reach the Python server through the proxy, the web container’s port stays unpublished and a blank project waits for its entry file (manage.py,main.py…) instead of crash-looping. A Node dev server next to Python keeps<project>-dev.<base>- Django/FastAPI backend plus Vite frontend. - Python templates: Django (
startproject config, settings prepared for the proxy andDATABASE_URLvia dj-database-url, psycopg and mysqlclient installed), Flask and FastAPI - each creates the.venv, installs the packages and pinsrequirements.txt. - Python actions (
python --version,python -m venv,pip install -r requirements.txt,pip freeze,uv sync,uv lock, Djangomigrate,makemigrations,collectstatic,check,flush) and worker presets (Python script, Python module,manage.pycommand, Celery worker, Celery beat) in the Python image; SSH user<project>.python; PyCharm/VS Code interpreter hints and a debugpy card (published port, path mapping, command lines) on the IDE tab;pythonPresetson/api/v1/runtimes; MCPcreate_projecttakespythonVersion,pythonServer,pythonPreset,pythonApp,pythonPort,pythonMode. Backups skip.venvand__pycache__with the other dependency caches. The weekly runtime-version workflow and the image builds cover Python like PHP and Node. The debugpy port does not depend on the application server: a tooling container publishes it too, because what a developer steps through is usually a management command or a script started from the terminal, and debugpy attaches to whatever process you launch. - A project says so when its virtual environment no longer matches its Python
version. The
.venvlives in the project directory and survives a container recreate, but it is built for one minor version - after a change from 3.13 to 3.14 its packages sit inlib/python3.13/site-packages, where the new interpreter does not look, and the application server starts only to fail on its first import. site-packages cannot be moved across minors, so the project carries a warning naming both versions and the action that rebuilds it (uv syncfor a uv project, elsepip install -r requirements.txt). It disappears by itself once the environment is rebuilt. - Cron jobs: any command on a schedule, not only the Laravel and Symfony worker
presets. The new Cron tab builds the schedule (every few minutes, hourly, daily,
weekly, monthly) or takes a cron expression and shows the next runs; templates
start from the Laravel scheduler, a Symfony or Django command, an npm script or a
script. Envoryx runs the command in the project’s PHP, Python or Node.js container
as the project owner, through
sh -cand bounded by a timeout (default 10 minutes), only while the project runs and never twice at once. “Run now”, the last 20 runs with their output, and acron.failednotification. Schedules use Envoryx’s time zone (TZ). Duplicating a project with its workers copies the jobs. - RabbitMQ as an optional service next to Redis and Mailpit (4.3, or 4.2), in the
wizard, on the Services tab, in
envoryx project create --rabbitmqand the MCPcreate_projecttool. The broker keeps its data in a volume, the management UI is published on a port of its own, the AMQP port on request. Envoryx generates a login and injectsRABBITMQ_HOST,RABBITMQ_PORT,RABBITMQ_USER,RABBITMQ_PASSWORD,RABBITMQ_VHOSTandRABBITMQ_URL(amqp://…@rabbitmq:5672/%2f) - the names laravel-queue-rabbitmq reads, and a URL for Symfony Messenger, php-amqplib, amqplib and Celery.MESSENGER_TRANSPORT_DSNstays yours to set - in Symfony’s.env,MESSENGER_TRANSPORT_DSN=${RABBITMQ_URL}/messages- because injecting it would quietly move a Doctrine transport to AMQP. The password is shown on the Services tab on request (operate scope, like database credentials); the IDE tab lists the connection for desktop clients next to the database and Mailpit. - Memcached as an optional cache next to Redis - in the wizard, on the Services tab, in
envoryx project create --memcachedand the MCPcreate_projecttool. It keeps everything in memory (no volume, so removing it needs no confirmation), publishes its port on the host on request and injectsMEMCACHED_HOST,MEMCACHED_PORT(what Laravel’s memcached store reads) andMEMCACHED_URL(memcached://memcached:11211for Symfony’s MemcachedAdapter). - The PHP images carry the
redis,memcachedandamqpextensions, switched off like the others. Laravel talks to Redis through phpredis unless told otherwise, and Symfony’s AMQP transport and MemcachedAdapter need their extension, so Redis, Memcached and RabbitMQ were only half usable from PHP. The wizard switches the extension on together with the service, adding a service on the Services tab does the same, and a service whose extension is off offers to switch it on. The images are rebuilt when this reachesmain; until a project runs the new image, PHP logs that it cannot load the extension. - Meilisearch and Typesense as optional search engines - in the wizard, on the Services
tab, in
envoryx project create --meilisearch/--typesense, the MCPcreate_projecttool and the logs endpoints. Both keep their index in a volume and run with a generated key that only leaves the backend through the operate-scoped/meilisearch/credentialsand/typesense/credentialsendpoints (“Show master key”/“Show API key” on the Services tab). Meilisearch 1.54 always publishes its port, where its web dashboard lives; Typesense 30.2 publishes on request. The application gets the names Laravel Scout reads (MEILISEARCH_HOST/MEILISEARCH_KEY,TYPESENSE_HOST/TYPESENSE_PORT/TYPESENSE_PROTOCOL/TYPESENSE_API_KEY) plusMEILISEARCH_URL/MEILISEARCH_API_KEY(Symfony’s meilisearch-bundle) andTYPESENSE_URL.SCOUT_DRIVERis left to the application, so a project indexing with another driver does not silently switch. The IDE tab lists both connections. - OpenSearch as an optional Elasticsearch-compatible search engine (3.8, or 2.19) - in
the wizard, on the Services and IDE tabs, in
envoryx project create --opensearch, the MCPcreate_projecttool and the logs endpoints. It runs as a single development node with its indices in a volume, over plain HTTP without login (security plugin off) and with a 512 MB heap (about 1 GB of RAM); the port is published on request. The application getsOPENSEARCH_HOST,OPENSEARCH_PORT,OPENSEARCH_SCHEMEandOPENSEARCH_URL(http://opensearch:9200).ELASTICSEARCH_*is left to the application: current Elasticsearch clients refuse to talk to OpenSearch. - OpenSearch Dashboards as an option of OpenSearch - a checkbox in the wizard and on the
OpenSearch card of the Services tab,
envoryx project create --opensearch-dashboardsandopensearchDashboardsin the MCPcreate_projecttool. The web UI (Dev Tools console, index management, Discover) gets a host port of its own and a link on the Services and IDE tabs; it always runs OpenSearch’s version and goes when OpenSearch goes. The image is about 2.6 GB, the container needs roughly 400 MB of RAM. - Command line. The Envoryx binary is now its own client:
envoryx project list/show/create/start/stop/restart/delete/logs/exec/run,envoryx backup list/create/restore/download/delete,envoryx git status/pull/checkoutandenvoryx login/logout/whoami. Everything goes through the REST API with an API token, so a token’s scope and project restriction apply exactly as they do in the web interface and for MCP, and every command lands in the audit log under the token’s name - the CLI has no database handle and no Docker socket of its own. On the host the container’s binary is enough (docker exec -it envoryx envoryx project list): inside the container the address is known and only the token is missing. Elsewhereenvoryx login --url …checks the token before storing it in~/.config/envoryx/cli.json(mode 0600), andENVORYX_URL/ENVORYX_TOKENwork without a file at all, which is what CI wants. A project is named by its name, its slug or its id;--jsonhands the API’s own answer tojq;--ca-certtrusts the local CA on a workstation. Commands exit 0 on success, 1 on failure and 2 on a usage error. envoryx project exec <project> -- <command>runs a command in a project container and hands its exit code to the calling shell, soenvoryx project exec shop -- php artisan migrate --force || rollbackdoes what it reads like. stdout and stderr stay apart, stdin is piped in (up to 512 KiB), and the command runs as the project owner in the project directory - where the browser terminal also starts. It is backed by a new endpoint,POST /api/v1/projects/{id}/services/{kind}/exec, which runs without a pseudo-terminal and answers with newline-delimited JSON frames (stdout,stderr, thenexit); nothing is written before the first frame, so a container that is not running is still an ordinary HTTP error. The terminal WebSocket stays what it is - a PTY for humans, where the streams are merged and the exit code is lost; scripts need the opposite, and a big pipe or an interactive shell still belongs in SSH.- Duplicate a project. Duplicate on the project page copies an existing
project into a new one -
shop→shop-test- with its configuration: runtimes and their settings, web server, services, environment variables, workers and the repository binding. The parts that hold data are checkboxes and default to on: the project directory (withoutvendor/,node_modules/and the other regenerable directories unless asked), the contents of the database and the objects of the bucket. Extra domains and the backup schedule are never copied - host names are unique, and a copy made to try something out should not inherit the original’s scheduled backups. The copy is its own project in every way that has to be: new id, slug, directory, network, volumes, containers, and a fresh host port wherever the original published one. What it keeps are the generated credentials - database name, user and passwords, the bucket and its keys - because each project has its own server, network and volume anyway, while a.envthat lives in the project files would otherwise point into the void, and the dump restores one to one (a renamed database would need--nsFrom/--nsTofor MongoDB and rewritten grants elsewhere). Nothing goes through a temporary file: the files are copied straight across, the dump of the original is piped into the client of the copy, and the bucket is read object by object. A database or storage container that is not running is started for the transfer and stopped again afterwards, so a stopped project can be copied as it is, and a copy is not started unless that was asked for. Any failure rolls the copy back completely, including the directory it created.POST /api/v1/projects/{id}/duplicateis the endpoint (admin scope; a token confined to particular projects may not create new ones),envoryx project duplicate shop "Shop Test" [--no-files|--no-database|…]the command,duplicate_projectthe MCP tool. - Rename a project. Until now the identifier a project was created with was final: the
displayed name could be edited, but
shop.test,envoryx-shop-phpand the databaseshopstayed whatever they were. Rename on the project page (andenvoryx project rename shop "Acme Blog" --yes, andrename_projectover MCP) now moves the lot: identifier and display name, the URL and the-dev/-s3host names, container, network and volume names, the SSH users the IDE connects with, the project directory, the backup directory and the rollback image tags. The database, its login and the object storage bucket travel too unless Keep the database and bucket names says otherwise - handy when a committed.envor an external client has the old name written into it. Docker can rename none of these, so the containers and the network are recreated from the new plan, the volumes are copied into their new names with a throw-away container from the project’s own web image (nothing is pulled) and the old ones removed. The database moves the way it has to: PostgreSQL renames database and role in place, the others create the new database and stream the dump of the old one into it before dropping it, and MongoDB maps the namespace on the way. The bucket’s objects are copied into the new bucket and the old one is dropped. The order is chosen so that a failure costs as little as possible: everything that can be checked is checked before the first container stops, the project record is renamed before any data moves and put back when the move fails, and old data is only dropped once the new copy is complete. A project that was running is running again at the end, and the current identifier has to be typed out to start any of it. - Database snapshots and cloning - the two things a day of development keeps asking for:
the dump you take before a migration, and the data of another project in your own.
Snapshots on the project’s Database tab dumps the primary database and nothing else,
with a note like “before the orders migration”, and puts it back with one click. The
project does not have to be running for either: a stopped database container is started
for the dump or the import and stopped again afterwards. Snapshots are ordinary backups
under
/config/backups/<slug>/- they show up in the Backups tab, can be downloaded and restore through the same verified path - so the dump a database version upgrade insists on appears among them too, ready to be put back. They roll: the ten newest of a project are kept, so taking one before every migration does not fill the disk, and scheduled backups and anything made by hand are never touched by that. Clone from another project replaces this project’s database contents with another’s - staging into local - as long as both run the same engine. The dump is piped straight from one container’s client into the other’s, so nothing is written to disk in between and a project of a few hundred megabytes is done in seconds. The source is only read, both projects are locked for the duration, and the target is snapshotted first unless that is switched off, so there is a way back. Overwriting a database asks for the project’s identifier to be typed out, as restoring does. On the command line:envoryx db snapshot shop --note "before the migration",envoryx db snapshots shop,envoryx db restore shop <id> --yesandenvoryx db clone local --from staging --yes(--no-snapshotskips the safety net). Over MCP an assistant can take and list snapshots (create_snapshot,list_snapshots); putting one back and cloning over a database stay out, like restoring a backup. - SSH remote forwarding (
ssh -R) into project containers. A process in the container reaches the client through a port on the container’s localhost: socat listens there and connects each connection to Envoryx on the project network, which accepts only the container’s address and hands the connection to the client. PyCharm’s SSH interpreter needs this to run anything. The listener ends with the forward or the SSH connection. - The IDE tab explains how to open a project in PhpStorm, WebStorm & co. over SFTP.
The embedded SSH server has served SFTP all along, but nothing said so, and the
obvious route was the network share. A new card lists the deployment settings
(host, port, user, root path
/var/www/html, web server URL) and walks through PhpStorm’s New Project from Existing Files wizard, checked step by step against PhpStorm 2026.2: a local copy that uploads on save, and how to fetch whatcomposer installornpm installchanged in the container. The host key is now also shown as MD5, the form PhpStorm asks you to confirm - the SHA256 value alone could not be compared. - Project containers resolve the project domains.
http://shop.testused to fail inside a container unless the Docker host itself asked a DNS server with the wildcard entry, and with Envoryx on its ownbr0IP the address was unreachable from there anyway. Envoryx now carries every host name the proxy serves as a network alias on each project network, so Docker’s DNS answers them with Envoryx’s address on that network. An application can call its own URL, and projects reach each other. Names added meanwhile arrive when a project next starts. - DNS setup guide under Settings → Domains & HTTPS. A single wildcard entry sends every project name to Envoryx. The card shows that entry for AdGuard Home, Pi-hole, dnsmasq/OpenWrt and Unbound (pfSense, OPNsense), with Envoryx’s address already filled in and a copy button. For routers without wildcard records, such as a FritzBox, it shows the hosts-file line with every current project name. DEPLOYMENT.md no longer sends Pi-hole users to Local DNS records, which cannot hold a wildcard.
- Tidier Unraid Docker tab. Envoryx’s containers now carry the Envoryx icon
(
net.unraid.docker.icon) instead of the question mark, and can go into a FolderView3 folder automatically: enter the folder name under Settings → General → Unraid Docker page and every project container, and the database browser, gets the labelfolder.view3=<name>. Create the folder in FolderView3 with the same name. Labels are set when a container is created, so projects move into the folder the next time they start. Without the setting nothing is recreated. The icon appears whenever a container is next created anyway, for example after a runtime update. A FolderView3 folder with the regex^envoryx-works too, with no setting at all.
Fixed
-
PyCharm’s SSH interpreter could not be set up. It uploads the project to
/tmp/pycharm_project_*before its sync folder can even be changed, and SFTP only knew the bind mounts; its probes also ran$SHELL -l -c …with an empty$SHELL. Outside the mounts SFTP now behaves like a server on the container (stat, list, read, write, mkdir, rename, delete, chmod run there as the project user, so a client gets what a shell over the same access gets), failures carry the codes a real server sends, and SSH sessions getSHELL=/bin/sh. The IDE tab describes PyCharm’s wizard (sync folder/var/www/html) and WebStorm’s “JavaScript Runtime” page as they are in 2026.2. -
SSH logins from an IDE failed with “Invalid credentials” (JetBrains Gateway) or a broken connection when the password took more than 30 seconds to type. The IDE connects first and asks for the host key and the password afterwards, and Envoryx allowed only 30 seconds for the whole login. It now allows two minutes, the default of OpenSSH’s
LoginGraceTime. -
The IDE tab’s interpreter hints for PyCharm and WebStorm named menus that 2026.2 no longer has: PyCharm’s SSH interpreter sits under Settings → Python → Interpreter and needs PyCharm Pro, WebStorm calls the field “Node runtime”. Projects without a
.venvget/usr/local/bin/pythonas the interpreter path next to the.venvone. -
PhpStorm’s remote interpreter over the embedded SSH server reported “PHP version: Not installed”. PhpStorm checks over SFTP that the interpreter exists, and SFTP only knew the bind mounts, so
/usr/local/bin/phpwas missing; it then uploads its helpers to~/.phpstorm_helpers, and SFTP started in the unwritable/. SFTP now answers stat requests outside the mounts from the running container (metadata only; reading and writing stay confined to the mounts) and starts in/home/envoryx. SFTP errors no longer reveal where a file lives on the Envoryx side. The IDE tab now mentions the “+” in the interpreter dialog and the path mapping a remote interpreter needs. -
“I have no name!” in the terminal of a PHP container, and git over SSH failing there with “No user exists for uid 99”. The PHP container runs as root and php-fpm switches users itself, but terminal, actions,
envoryx execand SSH work in it as PUID:PGID. That uid only got a passwd entry in containers that run as it, and a newly created project got none in any container until its second start. The entry is now made on the first start, for the uid Envoryx works as, and made again before a terminal, action or command runs, so existing containers are healed without a restart. -
Templates and git on Unraid. The one-shot containers that scaffold a template or run git ran as PUID/PGID (99:100 on Unraid) without a passwd entry for that uid, so the Next.js template died in create-next-app (“template next failed at create-next-app: Node.js v24…”) and git over SSH failed with “No user exists for uid 99”. They now start as root, add the entry and drop to PUID/PGID before the command runs - the same entry the long-running containers already got.
-
A failed template now says why. The error showed the last line of the output, which for a crashing Node process is just “Node.js v24.x” and for npm the path of its log file. It now shows the thrown error or npm’s cause, and the last 40 lines of the output go to the Envoryx log.
-
MongoDB is offered as 8.2 and that is what a new project gets. The previous default 8.0 - and 7.0 - refuse to start on Linux 6.19 and newer (“MongoDB cannot start: Linux kernel versions 6.19 and newer has a known incompatibility with this version”, SERVER-121912), which is every current desktop and server kernel: the container went into a restart loop and the project never came up. Both older series stay selectable for hosts that run them; an existing project keeps its version, as a MongoDB major cannot be upgraded in place anyway.
-
An application server (Python, Node dev server) raced the database on every start: it came up while the database was still initialising, and anything that connects at boot died on the first try. The restart policy hid that for most servers, but Django’s
runserverdoes not exit - its autoreload parent survives the failed child, so the container stayed running and answered nothing until it was restarted by hand. The server now waits for the database port (socat, two-second retries, visible in the container log) before it starts, on a host reboot too. A project without a database keeps the exact command it had, so nothing is recreated for it. -
A new project with the default PostgreSQL 18 came up with a database container in a restart loop: the data volume was mounted at
/var/lib/postgresql/data, and the 18 image - which keeps its cluster in/var/lib/postgresql/<major>/docker- refuses to start when it finds a volume on the old path, even an empty one. From 18 on the volume takes/var/lib/postgresql(the layoutpg_upgrade --linkexpects); 16 and 17 keep the data directory itself, so existing volumes stay where they are. A major upgrade was already refused for PostgreSQL, so no data moves. -
A rollback target that had left the host (
docker rmi, a prune on an installation that predates the rollback tags) made every reconcile - once every 30 seconds - retry the tag and log rollback image not protected. The image cannot come back, so the history now forgets it on the first miss (one info line), and the project stops offering a rollback that could only fail.
0.5.0 - 2026-09-22
Added
- Projects without PHP. The first wizard step asks for the runtime - PHP
application, Node.js application or Static site - and PHP is no
longer required. For a Node.js project the dev server is the application:
https://<project>.<base>, extra domains and<project>-dev.<base>all reach it through the proxy (HMR included), the project’s direct port is the node container’s host port, and a blank project waits for apackage.jsoninstead of crash-looping. Git, templates, actions, the terminal, the IDE tab (WebStorm/VS Code over SSH with user<project>), database, Redis, Mailpit and object storage variables all work without a PHP container. A static site is the web server alone with a starterindex.html. - Node templates: Vite + React (TypeScript), Next.js (App Router,
TypeScript) and Nuxt (Nuxt 4, minimal template) scaffold in a one-shot
container from the Node image and preset the dev server; the Vite template
sets the document root to
distfor later static builds. - Nuxt preset for the dev server (
--host --port, default port 3000); each preset carries a default port that the wizard fills in when you switch. - SPA fallback to index.html for static sites (Web server card and
wizard): unknown paths return
index.htmlso client-side routers survive a reload. - API:
serves(php/node/static) andappServiceon projects,web.spaFallback,nodePresetsand template runtimes in/runtimes; MCPcreate_projectacceptsnodePreset,nodeScript,nodePortandnodePackageManager, its output carriesserves,devUrland adirectUrlthat points at the dev server for Node-only projects;get_logsdefaults to the application container. New actionnode -v. - Mailpit also injects
SMTP_HOSTandSMTP_PORTnext to theMAIL_*variables (Node mailers usually read those). - PHP can be added to or removed from a project after creation (Runtime tab
→ PHP → Enable PHP; API
php.enabled). Adding makes PHP the application (FastCGI, project URL, published HTTP port); removing hands the project back to the dev server or the static document root, pauses PHP workers and keeps files and worker definitions. - Node.js production build mode: the dev server can run as Production
build - every start runs the build script, then the serve script
(
start,previewfor Vite) withNODE_ENV=productionfor the serve process only. - Node.js workers: npm script (
npm run <name>) and Node.js script (node <file>) presets run from the Node image; the Workers tab offers the presets whose runtime the project has, and a worker whose runtime is missing pauses until it is back. - Node.js debugging: publish the inspector port on a host port of its own
(Runtime tab); the IDE tab shows host, port, path mapping and
package.jsonexamples for Next.js, Vite, Nuxt and plain Node. - Whatever Envoryx does on its own is visible: the dashboard shows a
dismissible notice with the projects it started again after a restart and
the orphaned resources it removed, notifications carry the new kinds
projects.resumedanddocker.orphans_removed, and the audit log reads in plain words - actions as labels instead ofdocker.orphans_removed, “Envoryx (automatic)” as the actor of automatic entries, and a details column with what was changed or removed. - Orphaned Envoryx containers and networks - left behind by a restored
instance backup or a wiped
/config- are stopped and removed by the reconciler about a minute after they appear, instead of lingering in the host’s Docker list. Volumes hold data and are never removed automatically; the Docker page lists them with a Remove button. Removals appear in the audit log asdocker.orphans_removed. - Settings → General → Projects and the Envoryx container: an opt-in that stops every running project when the Envoryx container is stopped (for maintenance, a host shutdown) and starts them again when Envoryx comes back - also after a reboot of the host. Off by default: the project containers stay independent of Envoryx as before. A restart Envoryx asks for itself does not bounce the projects. Give the Envoryx container a stop timeout that covers all projects (see DEPLOYMENT.md, Stopping and restarting).
- The logo leads to “The spatial foundry”, a short ASCII film about the build factory, with the version, update status and links to the documentation, release notes, source and licence below it.
Changed
- Actions of services a project does not have are no longer listed (a PHP-only project shows no npm actions, a Node-only project no composer/artisan ones); running an action of an absent service answers 409. The Node service badge reads Node.js 24.
- The bare SSH user
<project>lands in the application container: PHP as before, Node when the project has no PHP.<project>.phpand<project>.nodepick one explicitly on projects with both. PhpStorm configurations of PHP projects are unaffected. - While a Node dev server serves a project, the web container’s HTTP port is
not published - the document root would otherwise expose the project
root (
.env, sources) on the LAN. Turning the dev server off publishes the same port again. - Web server configs of projects without PHP deny dotfiles (
/.env,/.git/…) on Caddy and Apache as Nginx already did; PHP configs are unchanged. - Backups skip the framework build caches
.next/,.nuxt/and.output/by default, likevendor/andnode_modules/; Include dependencies covers them all. - The project list is a table again: name, stack, state, resources and actions sit in the same columns on every row, however many services a project has. Badges follow a fixed order (runtime, web server, database, extras), the address is shown without its scheme, and small screens get a two-row layout with the state under the actions. Restart is offered only while a project runs; a stopped project has Start.
Fixed
- One-shot containers (templates, git clone/pull/status) could “finish” with exit code 0 before their command had run: the exit wait was registered with Docker’s default not-running condition, which a freshly created container already satisfies, and the cleanup then killed the still-running scaffold. The wait now asks for the next exit. Found by the Vite template smoke; the effect was a template that left the project directory empty.
- Git clone, pull and status work on projects without PHP: the one-shot container now comes from the project’s Node image (or the default Node image for static sites) instead of failing for want of a PHP service.
- The starter page of a project without PHP is an
index.htmlthe web server can serve; previously anindex.phpwas written that only ever showed its source. - Existing Node-only dev-server projects (created with the Enable PHP box
unticked or MCP
phpVersion: "none"): the project URL now reaches the dev server. The node and web containers are recreated once at the next start (new command wrapper, unpublished web port);<project>-dev.<base>and the node host port keep working.
0.4.0 - 2026-09-20
Added
- Rescue commands for a lost login:
envoryx admin reset-password,logout-all,revoke-tokens,resetandusers, run from the container shell (docker exec -it envoryx envoryx admin …). They act on the live database, need no restart and land in the audit log ascli. The sign-in page links to the instructions. - The interface speaks eight more languages: French, Spanish, Italian, Dutch, Polish, Portuguese (Brazil), Russian and Ukrainian. Pick one in the sidebar; the browser language is used on first visit.
- Envoryx now shows what it is doing. Long actions - creating, starting,
restarting, applying settings, deleting, backups and restores - report their
current step (image pull with download progress, container recreation,
template scaffolding …) in a panel at the bottom right, in the project list
and on the project page; the outcome appears there as well. The wizard shows
the same steps while a project is being created, and enabling JetBrains
Gateway explains that the container is recreated instead of pausing silently.
Actions started in another tab or by another user are visible too, and their
buttons stay disabled until they finish. (
GET /api/v1/operations,status.operationon projects)
Fixed
- Signing in after being sent to the sign-in page (session expired, direct link) now returns to the page you wanted instead of the dashboard.
0.3.0 - 2026-09-20
Added
- Envoryx has its logo: the
<E>mark and wordmark replace the placeholder icon in the sidebar, on the sign-in page, as favicon and as the Unraid template icon. Mint is the default accent colour; Settings → General → Appearance offers ocean, violet, amber and rose - buttons, highlights and the logo follow. The choice is stored per browser, like the theme. - Settings are organised in tabs (Diagnostics, General, Domains & HTTPS,
Access, Notifications, Backups, Tools, Audit log). The new Diagnostics
tab runs 14 set-up checks - Docker, storage, host paths, disk space, backup
directory, database integrity, host for project links, proxy ports, wildcard
DNS, SSH, HTTPS, version, project/Docker consistency, notifications - and
lists each finding with a fix; some fix themselves at the click of a button.
The dashboard shows a banner while something needs attention.
(
GET /api/v1/system/diagnostics) - Project backups include the object storage bucket: every object as a plain
file in
storage.tar.gz(content type kept as an extended attribute), restorable with or without emptying the bucket first. Scheduled backups and the MCPcreate_backuptool include it automatically.
Fixed
- The chosen theme was ignored on the sign-in and set-up pages (always light) and the app briefly flashed light on every load: the script applying the stored theme was blocked by Envoryx’s own Content Security Policy.
- When Envoryx runs with an IP of its own (Unraid
br0, macvlan) and no host for project links is configured, links to published ports - project URLs, Mailpit, the object storage console, database ports - silently pointed at Envoryx’s address, where nothing listens. The dashboard, the affected project tabs and the settings now say so and offer the Docker host that Docker reports as a one-click fix.
0.2.0 - 2026-09-19
Added
- S3-compatible object storage as an optional project service: one RustFS
container per project with a persistent volume, generated access keys, a
bucket named after the project created at start-up,
S3_*and Laravel/AWS SDKAWS_*variables injected, the S3 API served by the embedded proxy as<project>-s3.<base domain>for presigned URLs and public assets, a web console, and a switch for anonymous reads (bucket policy standing in for public-read ACLs). Wizard, Services tab and MCPcreate_projectknow it. - API tokens have scopes:
read(look, no secrets),operate(work with existing projects - start/stop, actions, backups, databases, git, SSH) andadmin(everything a browser session may do). A token can also be limited to particular projects; it then sees and touches only those and cannot create new ones. Scopes apply to the REST API, the MCP server and SSH/SFTP alike; refusals say which scope the operation needs. New tokens default tooperate; tokens issued before this release keep full access.
Fixed
- Deleting a project whose network is still used by a container Envoryx did
not create (for example one attached through Unraid’s network dropdown) is
now refused up front, naming that container, instead of removing the
project’s containers and volumes first and then leaving it in
failed. - The image a project can roll back to is kept under a tag
(
envoryx-rollback/<project>:<image>) instead of lying around untagged, sodocker image prune- Unraid’s “remove unused images”, clean-up plugins - no longer deletes it. Existing rollback targets are tagged at the next start; the tag moves on when a newer image supersedes it and goes with the project. - A backup interrupted by a crash or
kill -9no longer lingers: at start-up Envoryx removes project backup directories that never got their metadata and adopts complete ones whose database record was not written yet, so they show up and can be restored. Instance backups are written under a temporary name and renamed when complete, so a truncated archive can never be mistaken for a good one; leftovers are removed at start-up as well.
0.1.0 - 2026-09-19
First tagged release. Everything below is new.
Projects
- Single-container deployment for Unraid and Linux Docker hosts, embedded web UI (English, German), local admin account, sessions, audit log.
- Per-project stacks from a fixed catalogue: PHP 8.1-8.6 (Envoryx images with toggleable extensions, Xdebug), Caddy/Apache/Nginx, MariaDB/MySQL/ PostgreSQL/MongoDB, Redis, Mailpit, Node.js toolchain with dev-server mode.
- Templates (Laravel, Symfony, WordPress), git clone with deploy key or token, environment variables, php.ini settings, workers (queues, schedulers, scripts), project actions (composer, artisan, npm …), live logs, browser terminal.
- Embedded reverse proxy with local CA or Let’s Encrypt (Cloudflare DNS)
wildcard:
https://<project>.testplus custom domains. - Project backups (database dump, files, configuration) with schedules and
retention, restore, download; optional separate
/backupsmount. - Instance backups of Envoryx itself (database, CA, keys, settings), taken automatically before schema upgrades and restores; restore with in-place restart; import on another host.
- Embedded SSH/SFTP server for IDE remote interpreters, IDE tab with Xdebug and JDBC settings, optional JetBrains Gateway support.
- Notifications (ntfy, Discord, Slack, Telegram, e-mail, webhook) for unhealthy projects, failed creations, backups and certificate renewals.
- MCP server for AI assistants with personal API tokens.
- Image rollback: after a rebuilt image tag was applied by a restart, the Overview offers Roll back to the previous image; the previous image is kept out of pruning.
- Database browser: optional Adminer container shared by all projects, opened from the Database tab already logged in, served under the Envoryx UI behind the session.
Reliability
- Startup refuses corrupt databases and network filesystems for
/config(FUSE warning for/mnt/user/appdataon Unraid), watches disk space, restarts crashed background tasks and reports failed starts. - Project operations run to completion when the browser tab closes or the
connection drops; on
docker stoprunning operations getENVORYX_SHUTDOWN_GRACE(8 s) to finish and are otherwise recorded as interrupted instead of failed. - SQLite in
synchronous=FULLmode for power-loss safety.
API
- REST API accepts personal API tokens as
Authorization: Bearerfor scripts and CLIs; tokens cannot change the password or manage tokens. - Daily update check against GitHub releases (
ENVORYX_UPDATE_CHECK=falsedisables it); the dashboard and Settings show when a newer release exists.