Deplo

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: true alone is enough to mount the host's disk and chroot into it.
  • pid: host puts nsenter -t 1 one command away.
  • devices: hands over a raw disk.
  • cap_add, security_opt and userns_mode remove the boundary a step at a time.
  • A top-level volumes: entry with external: true attaches 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 wantedDo this instead
A bind mount for uploadsA Volume. Deplo creates and keeps it.
A bind mount for a config fileA File, edited in the browser.
network_mode: host for performanceA domain through Traefik.
A published port so another app can reach itNothing. Apps in one environment, on one server, reach each other by name.
A port for a game server, a mail server or raw TCPPublished ports. That is what the grant is for.
privileged because the README said soCheck 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: [] and privileged: false do 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?

On this page