What changed, release by release.
Versions follow semver. The panel shows v<version>+<commit> in its sidebar footer and agents update themselves from the panel build they connect to.
Email: account confirmation, password reset, security notices and email alerts.
Email through Cloudflare Email Service (CLOUDFLARE_EMAIL_ACCOUNT_ID, CLOUDFLARE_EMAIL_API_TOKEN, sender MAIL_FROM, default dockxo <[email protected]>). Without them nothing changes.
- Forgot password: “Forgot password?” on the sign-in page emails a reset link that works once, for one hour. The answer is the same whether or not the address has an account; resetting signs every session out and leaves two-factor as it was.
- Email confirmation for new accounts: a new account confirms its address from a link before it can use the panel (it can still resend the link or correct the address). Accounts that existed before count as confirmed.
- Security notices: the account’s address is told about a sign-in from a new browser or address, a password change or reset, an email change (the old address is told), two-factor turned on or off, new recovery codes, and new API tokens.
- Email alerts: Settings → Notifications gets an Email section next to Telegram; monitors and service alerts can go to email addresses. An address other than the account’s own confirmed one receives a confirmation link first and nothing else until it is opened, so the panel cannot be used to mail strangers.
- Changing the account email now needs the current password, like changing the password; before, anyone with a signed-in session could change it, which a password reset would have made an account takeover.
Wording on the sign-in page.
- The sign-in page no longer calls dockxo a “self-hosted deploy panel”: the panel is a hosted service and your apps run on your own servers.
Fix release: service notifications on panels upgraded from 0.8.0.
- Fixed: the Notifications card of a service failed with a 500 on panels upgraded from 0.8.0. 0.9.0 added its table in the middle of the migration list instead of at the end; migrations are tracked by position, so a database that had run 0.8.0 took the new table for one it already had and never created it (service alerts could not record problems either). The table is now created by a migration at the end, which does nothing where it already exists.
Service notifications and database health alerts.
- Service notifications: a Notifications card on each service’s Settings tab chooses Telegram chats and what to hear about: a failed deploy (a successful one if you like), a container that stops without being stopped from the panel or keeps restarting, and when it runs again. Stops, restarts and deploys started in the panel stay quiet. The card also lists the service’s domains with their uptime monitor, or a button to add one.
- Database health alerts: managed MariaDB services are checked every minute: whether the database answers, whether a replica’s replication threads run (with the error when they stop) and how far behind it is (limit per service, 60 s by default), and whether connections pass 90% of the maximum. Each problem is announced once when it starts and once when it is over, and survives a panel restart. Agents get a light
dbHealthcheck for this; agents that have not updated yet are asked for the full report instead. - Monitors: adding a monitor offers every domain of the account in a searchable list, custom domains first, with load balancers and caches marked. Before, only the first 24 were shown, so load balancer domains could be missing.
Uptime monitors with Telegram alerts.
- Monitors (uptime checks with Telegram alerts): a new Monitors page in the sidebar, like Uptime Kuma. Add a monitor for any site (your own domains are offered with one click): the panel checks it every 20 seconds to an hour, follows redirects, checks the status code and optionally a keyword, and shows up/down, response time, the last checks as bars, uptime for 24 hours, 7 and 30 days, a 24-hour latency chart and incidents. After the chosen number of failed checks in a row the monitor goes down and a Telegram message says why (DNS, refused, timeout, HTTP 5xx, TLS…); another one comes when it is back, with how long it was down. Optional reminders while it stays down, a warning before the TLS certificate expires, and “Check now” / pause. The sidebar shows how many are down.
- Telegram notifications: Settings → Notifications holds the chats alerts go to. Paste a bot token from @BotFather, send the bot a message, and “Find chats” lists the chats to choose from; “Send test” checks it. The token is stored encrypted.
- Monitors never reach private addresses (localhost, private ranges, cloud metadata), including through DNS answers and redirects, since the panel is shared by many accounts.
Groundwork for dockxo-admin: one panel per database, an operator API and versioned images.
- One panel per database: the panel now holds a MariaDB named lock for its whole life. A second panel on the same database (a forgotten container, a copy started by hand) waits up to
DOCKXO_INSTANCE_LOCK_WAITseconds (60) for the first to stop, then refuses to start and names the holder, instead of quietly splitting the agents so that servers show offline on one of them. The lock is taken before migrations, so two panels never migrate at once; it dies with the DB connection, so a killed panel never leaves it behind. If the lock connection drops, the panel takes it back, or stops if another panel took it meanwhile.DOCKXO_SINGLE_INSTANCE=0turns it off.docker stopnow shuts the panel down cleanly and releases the lock at once. - Operator API for dockxo-admin: with
DOCKXO_ADMIN_TOKENset,/api/admin/health(version, commit, instance and lock, DB latency and migration state, agents, memory),/api/admin/stats,/api/admin/usersand/api/admin/serversgive a read-only view of the whole panel. Off, and a 404, without the token. - Versioned images: release tags also publish
ghcr.io/noneym/dockxo:vX.Y.Zanddockxo-agent:vX.Y.Z, so a deploy can pick an exact version and roll back to the previous one.
Env can be written as a .env file into the build, like EasyPanel's Create .env file.
- Create .env file: GitHub and Dockerfile services can have their env written as a file into the build folder before
docker build(Env tab, path.envby default, e.g.app/.envworks too), for apps that read a.envfile instead of the environment (Laravel, CodeIgniter, dotenv). The text goes in as typed, replaces a committed file of the same name, and is sent even when.dockerignorelists it; variables still reach the container as environment. EasyPanel’sdotEnvPathis carried over by the restore CLI and the in-panel importer.
Fix release for the EasyPanel restore CLI.
- Fixed: the EasyPanel restore CLI stopped with
spawn ENAMETOOLONGon backups whosecode/holds a whole app. It extracted every file undercode/by name in onetarcall, which passed the Windows command-line limit with thousands of files; it now extracts onlyservice.jsonandcode/Dockerfileand lists the rest from the scan.
Backups now carry the whole project, and a project can be restored from its bucket, on this panel or on a new one.
- Project restore: Projects → Restore lists every backup found in a storage bucket, grouped by project. Pick a run, enter the plan’s passphrase and review what comes back: services, volumes, domains, the data saved in that run and which of your servers each source server maps to (matched by IP, then name). The project is recreated as a new project with every service’s settings, env, volume mounts, domains, redirects and backup plans; ticked volumes are refilled and SQL dumps loaded, then the services are deployed in dependency order (database primaries, replicas, routers, apps, caches, balancers). Volumes that were not backed up come back empty with the same settings.
- Sealed project file in every backup: each run starts by saving
project.dockxo(all settings and secrets, sealed with a passphrase set on the plan; scrypt + AES-256-GCM) and a plainrun.jsonsummary, so a bucket is enough to find and restore a project even if the panel is gone. A plan may back up the config only. Plans made in 0.4.x have no passphrase: edit them and set one, until then their runs fail with a note saying so.
Fix release: the panel starts again on databases copied from MariaDB 11 to 10.5.
- Fixed: the panel could not start after 0.4.0 on a database copied from MariaDB 11 to 10.5. A copied database keeps its tables’ collation (
utf8mb4_unicode_ciafter the copy) while new tables got the server default (utf8mb4_general_ci), and InnoDB refuses a foreign key between the two, so the backup tables’ migration failed at boot. New tables now take the charset and collation of the columns they reference.
Scheduled project backups to your own S3-compatible bucket, and a calmer Settings page split into tabs.
- Project backups to S3-compatible storage: add buckets under Settings → Storage (Amazon S3, Cloudflare R2, Backblaze B2, Wasabi, Hetzner, MinIO; tested before saving, secret stored encrypted). A project’s Backups tab holds plans: which services, which of their volumes, a cron schedule in your time zone with a preview of the next runs, and how many backups to keep. Volumes are packed on their own server and uploaded straight to the bucket in 16 MB parts (flat memory, nothing through the panel); a managed MariaDB is backed up as a consistent SQL dump while it keeps running; other services can be paused while their files are copied, and a container left paused by an interrupted backup is resumed when the agent starts. History shows every run with its items, sizes, errors and signed download links; old runs past the retention are deleted from the bucket. Runs are jobs, so they can be followed and cancelled.
- Settings in tabs: Account, API tokens, Integrations, DNS, Storage, Servers and Appearance, each with its own address (
/settings/storage). - Project tabs: Services, Backups and Settings (Cloudflare) on the project page.
Managed MariaDB with replicas and a router, a cache proxy, Cloudflare DNS that follows your services, search across everything, and server moves that no longer run out of memory.
Databases
- Managed databases (MariaDB 10.5): new service kind with its own wizard (credentials, size, expose, initial data from another database over the network, an uploaded dump or a URL) and a Database tab: live status, databases, users with per-database grants and read-only option, dump import as a job, server tunables. Root is limited to the server’s containers; binlog is on from day one so replicas can follow.
- Replicas (phase 2): add read-only replicas of a managed database on any server (initial sync from a GTID dump, real-time replication, lag and errors in the panel); promote a replica to primary with the other members re-pointed automatically.
- Database router (phase 3): MaxScale in front of a cluster: one endpoint for apps, read/write split, automatic failover mirrored back into the panel, switchover from the Router tab; promote uses the router when present. Replicas gained Reconnect; promote exposes the new primary for cross-server followers.
Routing and load balancing
- Cache proxy: new service kind — an nginx disk cache in front of one service port or a load balancer (path rules, TTL, disk budget, honour-or-ignore backend headers, purge, hit statistics,
X-Cache-Status). It serves its own domains and, on the target’s server, can take over the target’s domains too; from another server it reaches the target through the target’s:8099entrypoint restricted to the cache’s IP, so the origin is never published. Balancers gain an internalX-Dockxo-Viarouter for their caches. - Balancer health: the Balancer tab probes every member from the balancer host (the same request the health check sends) and shows the answer, its latency, stopped containers and how the last quarter hour of real traffic was spread over the members, read from the proxy’s access log.
- Sticky sessions reach the container: a server running several containers of the balanced service used to alternate between them on every request, because the balancer can only pin the server. That second hop now carries the balancer’s own stickiness (cookie
dx_lbc) and health check, so a visitor stays on one container and a container that stops answering leaves the rotation.
DNS
- Cloudflare DNS per project: a project can hold a Cloudflare API token (checked, stored encrypted). Adding a domain can create or update its A record, proxied or not, and a proxied record in a Flexible-SSL zone switches the domain to HTTP only so it cannot redirect-loop. The Domains tab shows where each custom domain points, with “Point here” when it is not this server. When a service moves to another server, A records that pointed at the old server follow it, keeping their proxy setting; records pointing anywhere else are never touched.
- Cloudflare for the whole account: one token in Settings is used by every project that has none of its own (a project token still wins, e.g. for domains in another account); “follow moves” belongs to whichever token is in use.
- DNS check: Settings → DNS check reads every custom domain in the account from Cloudflare and compares it with the server its service runs on. Wrong IPs, missing records and Flexible-SSL redirect loops are listed with their own fix (point the record here, create it, or switch the domain to HTTP only), plus “Fix all” after a confirmation. CNAMEs, domains outside the token’s zones and services without a server are listed as not managed. Nothing changes until you click.
Servers and storage
- Moving a service’s data no longer exhausts memory: the sending agent streamed the volume archive through
fetch()with a web-stream body, which ignores backpressure and held the whole archive in RAM whenever the link was slower than packing; a large volume could take the source server down. It now packs with native tar (numeric owners) and compresses with pigz on several cores (gzip level 1 by default,DOCKXO_TRANSFER_GZIPto change) in a child process piped into a plain http(s) request, so memory stays flat. The receiving agent unpacks the stream into the volume while it arrives: no archive on disk, no second pass. The byte count and the gzip checksum reject a truncated transfer.DOCKXO_TRANSFER_DIRECT=0on a target falls back to store-then-unpack; mixed agent versions fall back automatically. The restore CLI had the same memory problem and is fixed. - Port allowlist: any published host port can be restricted to IPv4/CIDR sources (iptables in DOCKER-USER, persisted by the agent). The allowlist editor marks which sources are your own servers and offers the missing ones in one click.
Panel and security
- Search: Ctrl/⌘ K (or “/”, or the box at the top of the sidebar) opens a palette that searches projects, services and servers by name, domain, repo, image, server, IP, folder, kind and status; several words narrow the result, letters in order match names (“lbsub”), accents and dotless i fold. Results are grouped, matches highlighted, and the reason shown when the name alone does not explain a hit (“domain editor.beartshare.com”). Arrow keys and Enter navigate, Tab limits it to the current project, recent picks show on an empty query. The project page and the Projects list gain an inline filter (“/” focuses it). Everything runs on data already loaded;
/api/projectsnow includes each service’s domains and source. - Two-factor authentication: TOTP (Google Authenticator, Authy…) with QR enrolment, one-time recovery codes, second step at sign-in; API tokens unaffected.
- Admin role:
ADMIN_EMAILSmarks panel operators; the “Panel address & agents” tools (reconnect / move every agent) are admin-only and hidden from regular accounts. - Logs in colour: container, deploy and job logs render ANSI colours, bold and dim instead of raw escape codes; filtering and error highlighting work on the plain text.
Second milestone: everything needed to run real workloads day to day.
Deploys and jobs
- Deploy strategy per service: recreate (default, old container removed first) or rolling (zero downtime); a failed rolling container is removed instead of lingering.
- Docker layer cache survives between deploys (the previous image is kept;
DOCKXO_KEEP_IMAGES); “Rebuild without cache” anddeploy { noCache }. - Cancellable jobs: deploys, migrations, volume restores and permission fixes can be cancelled; the agent aborts builds, clones, transfers and helper containers. Deployments and services orphaned by a panel restart are marked
interrupted. - Deploy jobs show the commit message; the Dockerfile in use is always part of the build context even when
.dockerignorelists it.
Routing
- Redirects (Traefik
redirectRegex) for services and load balancers, with a whole-domain shorthand; hosts pinned by a rule are routed automatically. - Per-domain HTTPS switch: http-only domains are served on :80 with no certificate (Cloudflare Flexible); http→https redirect moved to per-router middleware.
- Load balancers accept any number of domains; https-aware health checks; Logs tab for balancers (Traefik health events + access log).
Services
- Env kept as raw
.envtext with a syntax-highlighted editor (comments, blank lines and order preserved);PATCH /envsets/unsets keys in place. - Advanced tab: connection details, port expose, container options (cap add/drop, sysctls, groups, command, memory limit), connectivity test from the service’s network position.
- Service clone into this or another project; folders to group services; database / load balancer / app colour categories (configurable, per-service override); load balancers listed first.
- Services addressable as
<service>.<project>in the API;deploy?wait=1.
Panel
- Projects first in the sidebar with a collapsible project tree; project rows show the servers they run on.
- Copy control next to every server IP; log views reconnect themselves and have a Refresh button.
index.htmlserved no-cache with immutable hashed assets; build commit embedded in the image.- API reference in
docs/API.md; API tokens.
Restore tools
- EasyPanel database backups (redis / postgres / mysql / mariadb / mongo) restore as image services with a data volume, credentials and exposed port.
Release 0.1.0
First working panel: multi-server agents over an outbound WebSocket, Traefik per server with automatic Let’s Encrypt, GitHub / image / Dockerfile / template services, load balancers, free wildcard subdomains, volumes and mounts, file manager, migrations between servers, EasyPanel backup import (web tool + CLI), persistent jobs, private servers.