Host access
Why Deplo never exposes a Docker socket, and how one grant gates everything that reaches outside a container.
What it is
Everything that lets a container reach outside its own sandbox and touch the server: bind mounts, kernel capabilities, devices, host namespaces, foreign networks and foreign volumes.
All of it sits behind one grant: "Bind server folders" in the
permission picker, canMountHostVolumes in the API.
Why one grant covers all of it
A Compose file is user-written YAML that reaches the agent almost verbatim. Gating only the obvious bind mount would leave a plain member one key away from root on the machine, because these are all the same capability wearing different clothes:
privileged: truealone is enough to mount the host's disk and chroot into it.pid: hostputsnsenter -t 1one command away.devices:hands over a raw disk.cap_add,security_optanduserns_moderemove the boundary a step at a time.- A top-level
volumes:entry withexternal: trueattaches an existing volume by its host name. Deplo's volume names are deterministic, so this reached other teams' data, and the control plane's own database. driver_opts: {type: none, device: /, o: bind}is a bind mount of the host, declared one level up from where a service-level check would look.
What is gated
Service keys
privileged, cap_add, devices, device_cgroup_rules, security_opt,
cgroup_parent, cgroup, pid, ipc, uts, network_mode,
volumes_from, env_file, oom_kill_disable, oom_score_adj,
group_add, logging, userns_mode.
Paths, not keys
Anything that names a file or folder is judged by the path, not by the
key: a source that starts with / or climbs with .. is on the server
and is gated; a relative one is your app's own file, inside the project
directory your stack gets to itself, and is free. That covers a service
mount, an env_file, a secrets:/configs: file:, and a build
context. So env_file: - .env and file: ./db_pw.txt need no permission,
while env_file: - /etc/app.env does.
Top-level blocks
A volumes: entry that is external: true, pins a name:, or uses
driver_opts to bind a path. A networks: entry that joins a network
belonging to something else - another Compose project's, or the host's own
segment through macvlan. A network Deplo owns is not one of these: it is
rewritten onto your app's own network instead of being refused, so it needs no
grant and reaches nothing.
Builds
A build: whose context or Dockerfile is an absolute or .. path, that
loads host SSH keys or agents, or that runs privileged.
Merges
extends: and label_file:, because they pull in keys from a file you
did not show, and those keys can be any of the above.
What a refusal tells you
A refusal names what tripped it - the key, or the mount - and who can turn the permission on. If you are told an app reaches the server and you cannot see where, that sentence is where to look.
What is never gated
Hardening is free. no-new-privileges, cap_drop and read_only are not
on any list and never ask for a permission.
That is deliberate: a permission prompt in front of the safer choice is one people learn to route around, and then they route around the one that mattered.
Publishing a port is a separate grant
canExposePorts covers publishing a container port on the host: an app's
Published ports, a ports: entry in a
Compose file, and a database's Host port.
It is separate because it is a different risk. A published port is reachable from the network, but it does not give the container the machine.
How to grant it
Both grants sit on the member, alongside their role: Settings -> Members -> the member -> Permissions.
Grant them to somebody who administers the servers. Not to everybody who deploys apps, and never as a shortcut to make one Compose file save.
What to do when you hit the wall
Nine times out of ten there is a supported answer that does not need the grant:
| You wanted | Do this instead |
|---|---|
| A bind mount for uploads | A Volume. Deplo creates and keeps it. |
| A bind mount for a config file | A File, edited in the browser. |
network_mode: host for performance | A domain through Traefik. |
| A published port so another app can reach it | Nothing. Apps in one environment, on one server, reach each other by name. |
| A port for a game server, a mail server or raw TCP | Published ports. That is what the grant is for. |
privileged because the README said so | Check whether it is needed. Many images copy it from an older example. |
Taking it away
Revoking the grant stops that person from saving anything new that reaches the server - and it also stops the apps they already saved from deploying again. Deplo remembers who authored a Compose file that reaches outside its container, and reads the grant against them on every deploy, including one a git push starts:
shop's compose reaches past its container, and whoever saved it no longer has
permission to let an app reach the server.Two ways to clear it: give the grant back, or have someone who holds it open the app's Compose and save it again, which makes the reach theirs.
Containers that are already running keep running. Revoking a grant is not a kill switch - it stops the next deploy, it does not stop what is up. Stop the app if that is what you need.
Limits and gotchas
- A key that is present but empty declares nothing.
cap_add: []andprivileged: falsedo not trip the gate. - Deplo's own render-time entries never trip it. The check reads what you authored, not the finished file. - An import can hit this too. Items needing a grant you do not hold land in the migration report rather than being created. - This is a real trust boundary, not a formality. Somebody with this grant can read every other team's data on that machine.
Save or deploy refused for a permission you don't hold? See Sign-in and access troubleshooting.
See also
Did this page help you?