Docker containers fight you when you try to edit files inside them. The image is read-only, the running container only has a thin writable layer on top, and most of the editors you'd reach for (vim, nano, sometimes even cat) aren't installed.
There's a fix for the immediate problem of getting bytes onto a file inside the container, and there's the harder question of whether you should be doing it at all. Both are below.
Edit with docker exec and a built-in editor
If the container already has an editor, this is the fastest path. Open a shell:
1docker exec -it <container> sh
Use bash instead of sh if the image is Debian or Ubuntu-based. Then run whatever editor exists:
1vi /etc/nginx/nginx.conf
Many minimal images ship with vi from BusyBox or vim-tiny, but Alpine and distroless images often don't. To check what's available without flailing around inside the container, list editor binaries from outside:
1docker exec <container> sh -c 'ls /usr/bin/vi /usr/bin/vim /usr/bin/nano /usr/bin/nano-tiny 2>/dev/null'
Listing the paths explicitly matters: brace expansion ({vi,vim,...}) is a bash feature and isn't supported by dash (Debian/Ubuntu's /bin/sh) or BusyBox ash (Alpine), so a brace-style query passed to sh -c silently returns nothing whether or not an editor exists.
If the container has vi (the most common case), you'll see something like:
1/usr/bin/vi
If anything prints, you have an editor. If nothing does, move on.
Install an editor in a running container
When the container has a package manager but no editor, install one. For Debian or Ubuntu-based images:
1docker exec -it <container> bash -c 'apt update && apt install -y nano'
For Alpine:
1docker exec -it <container> sh -c 'apk add --no-cache nano'
For Red Hat Enterprise Linux (RHEL), Fedora, or CentOS Stream:
1docker exec -it <container> bash -c 'dnf install -y nano'
The install lives in the container's writable layer, so it survives docker restart but disappears the moment you docker rm or recreate the container from the image. This is by design. Nothing you install this way is persistent.
There's a practical reason to prefer nano over vim here, by the way: nano's escape sequences (Ctrl-X, Ctrl-O) survive a flaky docker exec terminal better than vim's mode switching. If your terminal gets into a weird state during a vim session over docker exec, that's usually why.
Edit on the host with docker cp
If the container has no package manager (anything built FROM gcr.io/distroless/* or FROM scratch), installing an editor isn't an option. Copy the file out, edit it locally, copy it back:
123docker cp <container>:/etc/nginx/nginx.conf ./nginx.conf$EDITOR ./nginx.confdocker cp ./nginx.conf <container>:/etc/nginx/nginx.conf
This works on any container regardless of what's inside it, including images that don't have a shell at all. You're also editing with the same tools you use for every other file on your machine, which is the safest possible setup. The full docker cp reference covers behavior around symlinks, ownership, and special files if you hit edge cases.
One caveat: file ownership follows the source. If the container's process runs as a non-root user (most hardened production containers do), the copied-back file may end up owned by root and unreadable to the application. Fix it afterward:
1docker exec <container> chown nginx:nginx /etc/nginx/nginx.conf
For services that don't pick up config changes automatically, send a reload signal (docker exec <container> nginx -s reload) or restart the container.
The bigger question: why are you editing files in a container?
Once a week, this is fine. Three times a day, every container you touch, you're working against Docker rather than with it. The container filesystem is meant to mirror the image filesystem. The moment they diverge, you have drift between what's running and what's defined in version control, and the next deployment wipes out whatever you changed.
In development, the usual fix is a bind mount, where the file lives on your host and the container reads it directly:
1docker run -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro nginx
Edit on the host with whatever editor you like. The container picks up the new file immediately, or after a reload depending on the service.
For configuration that varies between environments, mount it as a Docker volume or, in Kubernetes, as a ConfigMap:
12345678910111213141516171819202122232425apiVersion: v1kind: ConfigMapmetadata:name: nginx-configdata:nginx.conf: |worker_processes auto;# ...---apiVersion: v1kind: Podmetadata:name: nginxspec:containers:- name: nginximage: nginx:1.30volumeMounts:- name: configmountPath: /etc/nginx/nginx.confsubPath: nginx.confvolumes:- name: configconfigMap:name: nginx-config
The ConfigMap is declarative, lives in version control, and rolls out to pods on their next restart. (Worth knowing: because this example uses subPath, Kubernetes mounts a copy of the file at pod creation, so ConfigMap edits don't reach the running pod until it's recreated. Mounting the whole ConfigMap as a directory avoids this, at the cost of replacing everything under the mount path.)
For changes that have to survive docker rm and reproduce on every machine that pulls the image, edit the Dockerfile and rebuild. There isn't another way to do this; anything else is drift waiting to happen.
Common pitfalls
Read-only root filesystems. Containers started with --read-only (a sensible production hardening flag) won't let you write to the filesystem at all, even as root. The flag is correct. What's wrong is the assumption that the filesystem should be writable. Bind-mount the path you need to modify, or use an emptyDir volume for anything that needs to be writable but ephemeral.
Distroless and scratch images. docker exec -it <container> sh fails with exec: "sh": executable file not found because there's no shell to run. docker cp becomes your only option, and you can't inspect the file inside the container either. Copy it out, examine it on the host, copy the edited version back.
Changes that vanish after restart. If your edit disappears after docker restart, something is overwriting the file at container start. This is often an entrypoint script that templates a config from environment variables (common in Bitnami and official database images), or an init container in Kubernetes that recreates the file. Look at the entrypoint, not at Docker. Whatever source the entrypoint reads from is what needs to change; the file it writes will keep getting recreated.
Final thoughts
Editing files inside a running container is usually a symptom of missing observability rather than a routine workflow. If you're going in to change log levels, tweak a config, or chase down some behavior that looks wrong, what you really need is visibility into what the container is doing at runtime: the logs it's producing, the resources it's burning, and how its requests travel through everything around it.
Dash0's infrastructure monitoring pulls container metrics alongside logs and distributed traces through OpenTelemetry, so you can debug from the data instead of from inside the container.
Start a free trial to see your container fleet in one view. No credit card required.