July 12, 2026 · Docker HQ Team

Running docker exec From Your Phone Over SSH

Running docker exec over SSH from your phone works the same way it does from a laptop. You open an SSH session to the host, and the command runs against the remote Docker daemon. The pattern is ssh user@host "docker exec -it <container> sh" for a quick shell, or a one-off like ssh user@host "docker exec <container> cat /etc/hosts" when you just need output. Any SSH client on iOS or Android can do this, and a dedicated Docker app turns it into a couple of taps instead of typing flags on a touchscreen.

What docker exec actually does

docker exec runs a new process inside a container that's already running. It does not start a stopped container (that's docker start), and it does not create a new one (docker run). It attaches to the namespace of a live container and runs whatever you give it.

The two flags you'll use constantly:

  • -i keeps STDIN open, so the process can read input.
  • -t allocates a pseudo-TTY, so you get a real terminal with line editing and colors.

Together, -it gives you an interactive shell:

docker exec -it my-api sh

Reach for sh first. A lot of minimal images (Alpine, distroless-ish builds, scratch-based ones) don't ship bash. If sh works and you want bash, try docker exec -it my-api bash and fall back to sh when it's missing.

For a non-interactive one-off, drop the -t (and often the -i too) so you don't allocate a TTY you won't use:

docker exec my-api env
docker exec my-api cat /app/config.yaml

Running docker exec over SSH

Docker HQ's model and the manual approach both rest on one fact: the Docker CLI talks to a daemon, and you reach a remote daemon over SSH. You are not running Docker on your phone. Your phone runs an SSH client, the command lands on the server, and the server's Docker daemon does the work.

The most direct form runs the command and exits:

ssh deploy@prod-1 "docker exec my-api cat /var/log/app/error.log"

For an interactive shell you need SSH to allocate a TTY, which it does by default when the session is interactive. If you're forcing a command string, add -t:

ssh -t deploy@prod-1 "docker exec -it my-api sh"

Without ssh -t, an interactive docker exec -it will complain that the input device is not a TTY. That error trips up almost everyone the first time.

Use DOCKER_HOST instead of wrapping every command

If you'd rather run Docker commands as if the daemon were local, point DOCKER_HOST at the remote over SSH:

export DOCKER_HOST="ssh://deploy@prod-1"
docker ps
docker exec -it my-api sh

Now docker exec (and everything else) runs against prod-1 transparently. This rides on the SSH connection under the hood and needs a working SSH setup with key auth. It's the cleanest option if you SSH from a phone terminal like Termius or Blink and want plain Docker syntax. The same idea powers docker context:

docker context create prod --docker "host=ssh://deploy@prod-1"
docker context use prod
docker exec -it my-api sh

Getting comfortable with remote contexts is the same skill you need for running docker ps from your phone, so it carries over to every command you run remotely.

Doing it from a phone in practice

Typing ssh -t deploy@prod-1 "docker exec -it $(some container name you half remember) sh" on a glass keyboard at 3am is miserable. Two things make it bearable.

First, get the container name right before you type it. You almost never remember the exact name, and tab completion doesn't exist mid-string on a phone. List first:

ssh deploy@prod-1 "docker ps --format '{{.Names}}'"

Then copy the name into your exec command. If you can address the container by a short, stable name (set container_name in your compose file), this gets a lot easier.

Second, lean on a client that remembers hosts and keys so you're not retyping credentials. A general terminal app works. A purpose-built app works better because it removes the SSH plumbing and the flag-typing entirely. Docker HQ connects to your host over SSH and gives you a container list you tap into, then opens a shell without you writing -it or worrying about ssh -t. If you want the fuller picture of what tapping around a container looks like on mobile, the guide to managing Docker containers from your phone walks through the day-to-day flow.

Common things you'll run inside a container

Once you're in, it's an ordinary shell. The commands you reach for on a small incident:

# check what the process thinks its config is
cat /app/.env

# see if it can reach a dependency
wget -qO- http://db:5432 || echo "no route"

# check running processes inside the container
ps aux

# tail a log the app writes to a file
tail -f /var/log/app/current.log

Run a command as a specific user with -u, which matters when the container runs as non-root and you need to poke at root-owned files:

docker exec -u 0 -it my-api sh   # user 0 = root

Set a working directory with -w so you don't have to cd after landing:

docker exec -w /app -it my-api sh

And pass an environment variable for a single invocation with -e:

docker exec -e LOG_LEVEL=debug my-api /app/run-diagnostic.sh

Things that go wrong

"the input device is not a TTY." You asked for -t but the surrounding SSH session isn't a TTY. Add -t to the ssh invocation, or drop -t from docker exec if you don't actually need interactivity.

"executable file not found: bash." The image has no bash. Use sh. If neither exists (true minimal or distroless images), you can't get a shell at all. Get output with a specific binary instead, or debug from a sidecar. On newer Docker you can attach a debug shell with docker debug <container>, which mounts a toolbox even into images that ship nothing.

Container isn't running. docker exec only works on running containers. If it exited, docker ps -a shows it with a status, and you want docker logs <container> to see why it died, not exec.

Permission denied writing files. The container runs as a non-root user. Re-run with -u 0 if you genuinely need root. But remember: any change you make with exec is ephemeral and lives only until the container is recreated.

That last point is worth sitting with. Editing a file inside a running container with docker exec does not survive a docker compose up -d --force-recreate or an image redeploy. Exec is for inspecting and for temporary fixes, not for config that needs to stick. If you're changing something permanent, change the image or the compose file and redeploy.

Know before you exec: is the host even up?

The frustrating version of this task is opening your phone, starting an SSH session, and finding the host unreachable or the container already dead. Knowing that before you start saves the round trip. This is where push alerts earn their keep. Docker HQ can notify you when a host stops responding or a container goes down, so you're reacting to a specific problem instead of discovering it after three failed SSH attempts. You can compare how different mobile tools handle this in the rundown of the best Docker mobile apps. Pricing for the alerting and multi-host features is on the pricing page.

Frequently asked questions

Can I run docker exec over SSH without installing Docker on my phone?

Yes. Docker never runs on your phone. Your phone runs an SSH client that sends the command to the remote host, and the host's Docker daemon executes it. All you need locally is an SSH client and network access to the server.

Why do I get "the input device is not a TTY"?

You requested an interactive TTY with docker exec -it but the SSH session wrapping it wasn't allocated a terminal. Add -t to the ssh command (ssh -t user@host "docker exec -it ..."), or use DOCKER_HOST=ssh://user@host so the connection is handled for you.

How do I exec into a container that has no shell?

Minimal and distroless images often ship no sh or bash. You can still run a specific binary that exists in the image with docker exec <container> /path/to/binary. For a real shell, docker debug <container> (recent Docker versions) attaches a toolbox without modifying the image. Otherwise, debug from a separate container that shares the target's namespaces.

Is docker exec safe to use in production?

For reading state, checking config, and tailing logs, yes, it's routine. For changing files, be careful: exec changes are ephemeral and disappear when the container is recreated, so they mask the real problem rather than fixing it. Make lasting changes in the image or compose config and redeploy.

Next time you're away from your laptop, set up DOCKER_HOST=ssh://user@host on your phone terminal once and confirm docker ps returns. If that works, docker exec -it <container> sh will too, and you'll have the fix in hand before you'd have found your charger.