some examples of affected apps:
- nextcloud
- privatebin
- support
- yourls
some examples of affected apps:
slippery and off-topic indeed - put some iterations into it and made a new topic here. it's a subject that many, if not most, of us are interested in, has probably been well discussed before.
The below is the result of some back and forth with LLMs following our comment on this thread: https://forum.cloudron.io/topic/15616/hermes-agent/28?_=1788437686460
The analysis does not change our position - whilst 99.99% of the time it may be safe to run agents in a cloudron container, the relative ease by which (entirely preexisting) vulnerabilities may be exposed, ought to necessitate the consideration of perhaps running a separate cloudron instance for non-business critical services.
If your cloudron does not serve your company email, vpn, nextcloud, btcpay server etc - you can probably ignore the below.
If readers who are more technical, or well-versed in cloudron architecture and background can correct this analysis, and set things straight, we would appreciate and welcome the feedback.
-- nostr dev --
Generated 2026-09-05. Grounded in the Cloudron platform source (box 10.0.4, release tarball from releases.cloudron.io) and the docs' own App isolation claims.
This is a follow-up to the agent-isolation discussion in the Hermes Agent thread. It is a single-purpose, source-audited writeup: what the Cloudron platform actually puts between containers, what an administrator and a package maintainer can add, what remains, and a verdict for boxes that carry business-critical apps.
Threat model: the agent container is compromised (the realistic vector is prompt injection — the agent runs code that follows instructions found in web content). This document asks exactly one question: what can that compromised container reach on the box — other app containers, and the platform's admin services? Data exfiltration and harm to the agent itself are out of scope.
TL;DR: if the agent container falls, everything else on the box is network-reachable — every co-tenant's app port (by design: that is how the platform's nginx reaches apps), the shared database instances, the platform admin services, and the dashboard. Authentication is the only door, and part of the doors' hygiene is outside your control. Each claim below is verified against the 10.0.4 source with file references so it can be checked, corrected, or argued with.
This section lists the platform facts the analysis rests on, each verified in the Cloudron 10.0.4 source or docs.
cloudron (172.18.0.0/16) (src/docker.js, src/platform.js). The network is created with a subnet and gateway 172.18.0.1 — it is NOT --internal, and nothing filters container-to-container traffic (setup/start/cloudron-firewall.sh hooks INPUT only). A MASQUERADE rule is added so containers can also reach each other via the host's public IP.proxy_pass http://<ip>:<port> (src/nginxconfig.ejs). Consequence: an app's primary HTTP port MUST bind all interfaces (0.0.0.0) to be proxied at all — and is therefore reachable from every other container on the bridge, by design.auth_request, src/reverseproxy.js) is enforced by the platform's nginx on the PUBLIC route only. A direct connection to the container's bridge IP does not pass through it — the auth you face on that path is whatever the app itself implements.manifest.addons.docker; without the addon it returns 401, and it strips Privileged/CapAdd/Devices/SecurityOpt on create.This section describes what the platform itself puts between a compromised container and the other containers / admin services. These are what make ordinary multi-app co-tenancy safe.
1. Per-app credentials and authentication-only access to shared services. The platform creates unique credentials for each app on every addon (databases, mail, LDAP) and rotates them itself; every shared service accepts only authenticated access. What it stops: a compromised container can open TCP to any co-tenant and to the shared MySQL/PostgreSQL/Mongo instances, but it holds no valid credentials for them — "one app cannot access another app's database or files". On the flat, unfiltered bridge, authentication is the only inter-app barrier, and it is a real one. Its limits: it protects data, not reachability — and it only covers services that authenticate. The ldap addon hands over a valid directory secret, the docker addon unlocks the dockerproxy; and an app's own HTTP port has only whatever auth that app implements (see protection 3 for what SSO does and does not cover).
2. The container confinement stack applied to every container. Every app container is created read-only rootfs, runs as a non-root user, has NET_RAW dropped unless the manifest asks for it, and runs under the docker-cloudron-app AppArmor profile that denies mount and /proc, /sys writes. What it stops: tampering with the shared kernel interfaces and the common container-escape primitives — i.e. it protects the HOST, which is the admin service of last resort. Its limits, verified in source: the profile is deliberately a copy of Docker's docker-default (baseline, not hardened), and it does nothing to restrict network reach into other containers — a full TCP connect scan of the bridge is trivially possible from a compromised app.
3. Authenticated TLS proxy as the only public ingress. Each app is served on its own subdomain behind the platform's nginx with TLS; the whole container network (172.18.0.0/16) is unreachable from outside the server, no container port is directly exposed to the internet, and origin isolation means XSS in one app cannot steal another app's session. What it stops: infiltration of any app or admin surface from the public internet — attempts must originate from inside the box. Its limits: this is exactly the position a compromised agent container is in — inside the box, on the bridge, with every co-tenant and admin service routable. And SSO/proxyAuth are enforced on this public route only; they do not apply to a direct bridge connection to the container IP, where the only barrier is the app's own auth.
This section is what the box administrator can actually do, with the controls Cloudron gives them, to shrink the infiltration surface.
ss -ltnp and check listeners: you should see the app's httpPort on 0.0.0.0 (normal, required) and anything else that app bundles — a Redis, an internal DB, an admin panel — on 127.0.0.1 only. Anything else on 0.0.0.0 is a bridge-reachable door with only the app's own auth in front of it.This section is what a package maintainer controls in their own app's container — written for the agent package, but the same rules apply to every package.
net_admin (adds NET_ADMIN + NET_RAW), no ping (keeps NET_RAW dropped). Non-root user throughout.This section is what remains attackable — from the compromised agent container into other containers and admin services — even with the platform protections and both hardening seats applied.
1. Every co-tenant's primary port is reachable, by design, with only the app's own auth in front. The platform's nginx reaches apps by container IP; therefore every app's HTTP port binds all interfaces and every other container can connect to it directly. SSO/proxyAuth do not apply on that path. So the inter-app barrier for the typical web app reduces to the app's own login form — plus per-app credentials where the app talks to addons. An agent container can enumerate co-tenants (Docker DNS or a /16 sweep) and methodically try every front page and login. Detection: container-to-container flow alerts on the host. Mitigation: the audits above (know which of your apps have weak or default auth on their primary port); SSO wherever the app supports it, for the front door at least.
2. Platform admin services are permanently reachable from every container. Any compromised container can open TCP to 172.18.0.1:3002 (ldap), 3003 (dockerproxy), 3006 (app-bridge), 3007 (scim), DNS, and the dashboard on 443 — unconditionally allowed by the firewall. Without addons these reject the agent's requests (dockerproxy 401s, ldap demands the bind secret), but the attack surface never goes away: the dashboard login accepts attempts from container IPs (rate-limited to 10/s, not blocked), and an unauthenticated bug or info leak in any of these services would be reachable by exactly the workload most likely to look for it. Detection: the 172.18.x failed-login canary; container-to-172.18.0.1 flow alerts. Mitigation: 2FA and IP restriction on the dashboard; accept the network path as a platform constant and monitor it.
3. Kernel/container escape = the host = every admin service at once. The confinement stack blocks the common primitives, but it is deliberately baseline. Operator hardening does not change escape probability. A public runc/kernel escape lands on the host, which means: every container's /app/data, the platform database (all app credentials, user directory), the docker daemon itself, and the operator's SSH surface. This is the maximum-severity outcome of the infiltration question. Detection: host-side auditd on mount/nsenter/setns; file-integrity monitoring; alert on unexpected privileged processes. Mitigation: urgent patch posture; no SSH keys or VPN credentials stored on the box; snapshot for fast rebuild.
4. Every addon is a key into an admin or shared service. The "zero addons" rule is load-bearing and fragile: the moment the agent's manifest gains an addon for a feature (ldap for directory sync, docker for builds, sendmail for notifications), the platform mints credentials valid against an admin or shared service — and those live in the container env, readable by the injected process. Addon creep is the quiet way the infiltration surface regrows. Detection: review the manifest diff at every package update. Mitigation: treat every addon request on an agent package as a security decision.
5. The operator's session is the softest admin door. The agent has a web surface the operator logs into (dashboard, kanban, chat history). A prompt-injected agent renders attacker-influenced content in that surface — crafted links, file names, session summaries — aimed at the one admin who trusts it. The operator's browser session is a realistic path from "agent compromised" to "box admin compromised" that no network control addresses. Mitigation: keep the dashboard in a separate browser profile from the agent UI; 2FA everywhere; treat links and downloads coming out of the agent's UI as untrusted input.
This section is the reachability takeaway, then the verdict for business-critical boxes.
Cloudron's platform protections make ordinary multi-app co-tenancy safe: other containers' data is behind per-app credentials, the host is behind a baseline-but-real confinement stack, and everything admin-facing sits behind an authenticated TLS proxy on the public side. But for a compromised agent container the question is reachability, and the reachability facts are: the bridge is flat and unfiltered; every co-tenant's primary port is reachable by design (that is how the platform's nginx works) with only the app's own auth in front, since SSO/proxyAuth guard the public route alone; the platform admin services and the dashboard are unconditionally reachable from every container; and an addon minted for convenience is a key into an admin door. The practical posture: zero addons on the agent package, loopback for everything the platform does not need to reach, real auth on the primary port, a 2FA-hardened dashboard, urgent kernel patching, and detection tuned to the canaries that matter — container-originated admin logins, container-to-container and container-to-platform flows. The honest caveat from the source: the confinement stack is deliberately baseline, so the host itself remains defended mainly by patching speed.
Is the risk real if you run your business on the box? Yes — but it decomposes.
This writeup is the evidence base behind the position we stated in the Hermes Agent thread: the platform's protections assume the threat is outside the box, and an agent is a workload that reliably puts the threat inside it. That is why we run agent workloads off Cloudron (Firecracker microVMs on dedicated hardware, currently under evaluation). For everyone who does want agents on Cloudron, the two hardening sections above are the practical checklist — and if any of the file-cited facts is wrong, the source is public and we would genuinely like to know.
typically no more than 6 or 7
@andreasdueren we were initially tempted to use cloudron for packaging agents but given the many documented cases of AI breaking out of containers, compounded by the amount of unencrypted internal cloudron comms and ports being available, we backed away
We are now evaluating firecracker on a dedicated machine for agent hosting
Curious what your (and others) position is with regard to the risks of running agents on cloudron containers
@ikalou - we gave up on jitsi / mirotalk / nextcloud for group calls. These days we mostly use Proton Meet.
The Cloudron dashboard does not show disk usage (not in v10.0.2 nor earlier versions for at least a year). The request to:
/api/v1/system/filesystems
returns:
{
"status": "Internal Server Error",
"message": "Cannot read properties of undefined (reading 'contents')"
}
The root filesystem itself is healthy and reports usage correctly:
Filesystem Type 1B-blocks Used Avail Use% Mounted on
/dev/mapper/luks-a7030049-5aad-45e0-915c-3073a14fb4ac ext4 1883388502016 1367907303424 419734818816 77% /
The Box log contains:
shell: df: df -B1 --output=source,fstype,size,used,avail,pcent,target
shell: df: df -B1 --output=source,fstype,size,used,avail,pcent,target /home/yellowtent/platformdata
{
path: '/api/v1/system/filesystems',
status: 500,
error: HttpError: Cannot read properties of undefined (reading 'contents')
at getFilesystems (file:///home/yellowtent/box/src/routes/system.js:153:28) {
status: 500,
internalError: TypeError: Cannot read properties of undefined (reading 'contents')
at Object.getFilesystems (file:///home/yellowtent/box/src/system.js:112:42),
details: null
}
}
The relevant code in /home/yellowtent/box/src/system.js is:
for (const stdPath of standardPaths) {
const [dfPathError, diskInfo] = await safe(df.file(stdPath.path));
if (dfPathError) throw new BoxError(BoxError.FS_ERROR, `Error getting std path: ${dfPathError.message}`);
filesystems[diskInfo.filesystem].contents.push(stdPath);
}
The two df invocations identify the same root device using different names.
An unqualified df reports:
/dev/dm-0 ext4 ... /
A path-specific df reports:
/dev/mapper/luks-a7030049-5aad-45e0-915c-3073a14fb4ac ext4 ... /
These are aliases for the same device:
/dev/mapper/luks-a7030049-5aad-45e0-915c-3073a14fb4ac → /dev/dm-0
It appears that Cloudron initially indexes filesystems using the literal source returned by the unqualified df:
filesystems[dfEntry.filesystem] = ...
It then looks up the path-specific result using the other device alias:
filesystems[diskInfo.filesystem].contents.push(stdPath);
As a result, filesystems[diskInfo.filesystem] is undefined and the API throws while accessing .contents.
It may be necessary to normalize device paths before using them as keys, or fall back to matching by mountpoint/device identity.
Has anyone else encountered this with an mdadm + LUKS root filesystem? Is a fix planned for an upcoming Cloudron release?
good to know..
you can also run the management /dashboard component: https://ca.cloudron.io/app/wazuh
Your wish has been granted: https://ca.cloudron.io/app/wazuh
@necrevistonnezr - I guess you are not going to be needing BTC Pay Server any time soon. Thanks for sharing your nicely-worded (and referenced) opinions.
@necrevistonnezr - hardly. Coldcard are accountable for their catastrophic mistake. The community has also learned a valuable lesson with regards to RNG and entropy.
Note that the coldcard incident had nothing at all to do with the Bitcoin protocol, which continues to securely mine blocks.
Remember that the money you loan to the bank:
If you are running BTC Pay Server, please upgrade immediately, and destroy the macaroons file. There is an active exploit.
More info: https://github.com/btcpayserver/btcpayserver/releases/tag/v2.4.2
Image details (v2.4.2 is now available): https://ca.cloudron.io/app/btcpay-server
Our recommendation is to use a data-only e-sim (eg silent.link) and ditch the phone number. If you need SMS for 2FA, just use a web provider such as crypton.sh
fyi, for calendar you'll need a caldav bridge, davX is available on Zap Store
On Zap Store you can also see what apps others are recommending / installing (and zap the devs directly)
Pixel with Graphene OS, it's a no-brainer!
We are happy to block the IP of any country implementing dangerous policies like this
No responsible adult should allow their kids to upload personal identity documents to random (or worse, mainstream) websites
curious if anyone has tried it / has any feedback?
https://github.com/etkecc/ketesa
Billed as the replacement for Synapse Admin..
Routstr is now available as a community app: https://raw.githubusercontent.com/Routstr/routstrd-auth/refs/heads/cloudron/CloudronVersions.json
You can plug routstr in as a model provider to an array of AI clients, such as Roo Code, Claude, Opencode, Cursor, etc. Just use the url (eg your.cloudron.domain) and the api key.
There is no web app, it's just an API server. To set it up, use the terminal on the server, or install it locally with bun install -g routstrd
Instructions: https://github.com/Routstr/routstrd-auth/tree/cloudron#cloudron
Note - you have a choice of models, and you pay for them using Bitcoin over the lightning network. To top up your balance, run: routstrd receive xxxx where xxxx is an amount in sats. This balance will be shared across all the users, so it's a great way to provide no-kyc AI to your team.
same issue here. Moving to 8gb worked. subsequently resizing back to 2gb or 4gb does not work.
8gb is pretty big!
After being dissatisfied with existing time tracking solutions, we built our own. It is now available for others to try, and we'd love to get your feedback. We have been using it in production for ~6 months. Features include:
The first user to log in becomes the admin. You can set roles by project (eg owner, manager etc) to allow scoped visibility into hours tracked, and you can configure usage alerts by project or customer using a matrix API key.
https://git.nostrdev.com/trakstr/cloudron-trakstr/raw/branch/master/CloudronVersions.json
