A team adds sshd and cron to their app's Docker image for access and scheduled jobs. What do you argue, and what do you offer instead?
answer
- Both are habits from managing virtual machines
- The engine already provides one of these
- Ask what happens with three replicas
- In-place fixes drift from the image
- Same image, different command, own exit code
basics
~20 sBoth are VM habits. docker exec gives shell access with no second daemon, no keys in the image and no extra listener. Scheduled work belongs in its own container, run from the same image with a different command, so it has its own logs and exit code.
solid answer
~50 ssshd buys nothing the engine does not already provide: `docker exec` starts a process in the running container's namespaces, needs no host keys or accounts baked into a layer, adds no listener to every replica, and dies with the container -- whereas an ssh session invites people to patch a container in place so it drifts from its image. Offer `docker exec`, `docker logs`, `docker inspect` and `docker top`, plus a debug image when the runtime image has no shell. cron is worse: with three replicas the nightly job runs three times, cron must itself be running so it displaces the app as PID 1 or drags in a supervisor, its output never reaches `docker logs`, and its failures change nothing about container status. Run the job as its own container from the same image with a different command.
code
bash · 7 lines# shell access without sshd in the image
docker exec -it reranker sh
docker logs --tail 100 --timestamps reranker
docker top reranker
# the nightly job: same image, different command, its own container
docker run --rm --network reranker-net myorg/reranker:2026.09.1 reranker --rebuild-indexgo deeper
Be ready to name docker exec as the way into a running container and to say why sshd in an image is unnecessary. Knowing that images are meant to be immutable and disposable carries most of the answer.
Explain the mechanics you would cite: exec runs inside the existing namespaces and dies with the container, cron needs its own running process so it displaces or supervises the app, and a cron job's output never reaches the container's stdout.
Demonstrate the production judgement: replicas multiplying a scheduled job, credentials living in layers, listeners in every replica, and configuration drift from in-place fixes. Then offer the concrete replacement -- exec plus a one-shot container from the same image.
Own the access path rather than the argument. If people reach for sshd, the platform's debugging story is failing; decide what supported access and scheduled-job patterns you ship so no team has a reason to smuggle daemons into service images.
### Why the request comes up Both additions come from habits that are correct on a virtual machine and wrong on an image. On a VM you need SSH because there is no other way in, and you need cron because there is no other scheduler. A container has a different contract: the image is immutable and disposable, the engine already gives you a way in, and the thing that starts containers is already a scheduler. ### sshd: you already have a door `docker exec` starts a new process inside an existing container's namespaces and cgroup. It is the same access sshd would give you, with none of sshd's baggage: * **No second daemon.** sshd is a long-running listener that must be started somehow, which usually drags a supervisor into the image and with it every problem that implies -- the engine watches PID 1, and PID 1 is now not the application. * **No credential store in the image.** sshd needs host keys, user accounts and authorized keys. Baked into an image, those are secrets in a layer that anyone who can pull the image can read; injected at runtime, they are one more mount to get right. `docker exec` is authorised by access to the Docker socket, which is an access decision you already have to make and audit. * **No extra attack surface.** An sshd inside every replica is a network listener in every replica -- running, by default, as root, in a filesystem your security posture assumed nobody could log into. * **Correct lifetime.** An `exec` session dies with the container. An ssh session invites people to *fix things in place* -- edit a config, restart something, install a package -- so the running container drifts away from its image and the fix is lost on the next deploy. That drift is the deeper reason to refuse, not the port. What to offer instead: `docker exec -it <container> sh` for a shell, `docker logs` for output, `docker inspect` for configuration, `docker stats` and `docker top` for what it is doing, and -- when the image is deliberately minimal and has no shell -- a separate debug image built from the same base, or a throwaway container run from a tools image on the same network. ### cron: you already have a scheduler Baking cron into the application image is worse than it looks: * **It multiplies with replicas.** Three replicas of the service means three copies of cron, so a nightly job that must run once runs three times. Nothing in the image can coordinate that. * **It needs a second PID.** cron must be running for jobs to fire, so either the application is backgrounded behind cron or a supervisor is added -- the same trade discussed above, taken on for a job that runs once a day. * **Its failures are invisible.** A cron job's output goes to cron's own delivery mechanism, not to the container's stdout, so `docker logs` will not show it. A failing job does not change the container's status, does not set an exit code anyone reads, and does not trigger any restart policy. * **Its environment is not the application's.** cron runs jobs with a minimal environment, so the variables the application was configured with are usually absent -- a class of bug that appears only in production and only at 03:00. What to offer instead: run the job as its own container, from the *same image*, with a different command -- `docker run --rm <image> <job-command>` -- started by whatever already schedules work in that environment (a host timer for a single Docker host, a scheduled-job object in an orchestrator). Now the job has its own exit code, its own logs on its own stdout, its own resource limits, and its own alert when it fails, and it runs exactly once regardless of how many application replicas exist. ### Worked example A Rust recommendation re-ranker is built in a builder stage and copied into a slim runtime image; the build takes about 11 minutes and the team resists "another image" for that reason. But the nightly index rebuild does not need another image -- it is the same binary with different arguments, so it is the same image run with a different command. And the debugging that motivated sshd is `docker exec` plus the engine's own inspection commands. Nothing about the 11-minute build changes: one build, two ways to run it. ### Where to be pragmatic The argument is about *long-running production service images*. A developer sandbox image, a CI worker image, or a vendor appliance that ships its own process manager can legitimately carry more than one process -- those are deliberate exceptions with an owner, not the default for a service. And if a platform genuinely cannot offer `docker exec` to the people who need it, the fix is to fix that access path, not to smuggle a second daemon into every image in the fleet.
- The runtime image is minimal and has no shell at all, so `docker exec -it sh` fails. Now what?Do not add a shell back to the production image. Use the engine's own inspection surfaces first -- logs, inspect, stats, top -- then, if you need a filesystem or a debugger, run a throwaway tools container on the same network and, where you need the app's own view, build a debug variant from the same base image and run that in a controlled environment rather than in the fleet.
- The team says their scheduled job needs the application's warm in-memory cache, so it must run in the same container. Is that a valid exception?It is a real constraint, but the usual answer is to expose the job as an endpoint or admin command on the running service and have an external scheduler call it, rather than to add cron to the image. That keeps one process per container, gives the job an HTTP status or exit code someone can alert on, and still runs exactly once no matter how many replicas exist.
- What is the strongest single argument against ssh access into containers, if you only get one?Configuration drift. Once people can log in, they fix things in place -- edit a file, restart something, install a package -- so the running container no longer matches the image it came from, the fix is lost at the next deploy, and nobody can reproduce the state that was actually serving traffic. The extra daemon and listener are real costs, but that one destroys immutability.
saying these in an interview costs you the question
- Says containers need sshd because you must be able to log in
- Keeps host keys or authorized_keys baked into an image layer
- Runs cron in every replica of a scaled service
- Expects cron job output to appear in docker logs
- Fixes problems inside a running container and calls it done
- Believes a scheduled job requires a second image