docker run has dozens of options, but on a typical VPS you use the same eight or nine again and again. This guide explains each one with an example, then puts them together into a single command you can adapt. Once the command works, you can move it into a Compose file so it is easy to repeat.
Before you start
- A Linux VPS with Docker Engine installed. See Install Docker and Docker Compose.
- A user that can run
dockercommands (in thedockergroup, or usingsudo). - Basic familiarity with images, containers and tags.
The general shape of the command is:
docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARGS...]Options always go before the image name. Anything after the image name is passed to the container as its command.
-d: run in the background
Without -d, the container runs in your terminal and stops when you press Ctrl + C. With -d (detached), Docker starts it in the background and prints the container ID.
docker run -d nginx:stableUse -it instead when you want an interactive shell, for example docker run -it --rm debian:12 bash.
--name: give the container a name
Docker invents random names like quirky_turing unless you set one. A fixed name makes logs and exec commands easier:
docker run -d --name web nginx:stable
docker logs webNames must be unique. To reuse a name, remove the old container first with docker rm -f web.
-p: publish a port
-p HOST:CONTAINER forwards a port on the VPS to a port inside the container.
docker run -d --name web -p 8080:80 nginx:stableThe site is now on http://203.0.113.10:8080. To expose it only to the server itself, for example behind a reverse proxy, bind to localhost:
docker run -d --name web -p 127.0.0.1:8080:80 nginx:stableWarning: Published ports skip UFW's normal rules, because Docker writes its own firewall rules. Bind private services to 127.0.0.1 or read Fix UFW and Docker bypassing the firewall.
-v: keep data in a volume or folder
Files written inside a container are lost when it is removed. -v mounts storage that survives.
A named volume, managed by Docker:
docker run -d --name db -v pgdata:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=change-me postgres:16A bind mount, using a folder on the host:
docker run -d --name web -p 8080:80 \
-v /srv/site:/usr/share/nginx/html:ro nginx:stableThe :ro suffix makes the mount read-only inside the container.
-e and --env-file: environment variables
Most images are configured through environment variables:
docker run -d --name app -e TZ=Europe/London -e APP_ENV=production myapp:1.0For more than two or three variables, put them in a file, one KEY=value per line, and load it:
docker run -d --name app --env-file /srv/app/app.env myapp:1.0Keep that file readable only by its owner (chmod 600), because it often holds passwords.
--restart: come back after a crash or reboot
By default a stopped container stays stopped, even after the VPS reboots. Add a restart policy:
docker run -d --name web --restart unless-stopped -p 8080:80 nginx:stableunless-stopped restarts the container after crashes and reboots, unless you stopped it yourself. The other policies are covered in Docker restart policies.
--memory and --cpus: resource limits
On a small VPS, one runaway container can starve everything else. Cap it:
docker run -d --name app --memory 512m --cpus 1 myapp:1.0If the app goes over 512 MB, the kernel kills it rather than the whole server slowing down.
Other flags worth knowing
--rmremoves the container when it exits. Ideal for one-off tasks.--network NAMEattaches the container to a user-defined network so containers can reach each other by name.-u 1000:1000runs the process as a specific user and group ID instead of the image default.-w /appsets the working directory.--read-onlymakes the container's own filesystem read-only.
Putting it together
A realistic command for a small web app:
docker run -d \
--name app \
--restart unless-stopped \
-p 127.0.0.1:3000:3000 \
-v app-data:/app/data \
--env-file /srv/app/app.env \
--memory 512m --cpus 1 \
myapp:1.0The same thing in Compose is easier to keep and change:
services:
app:
image: myapp:1.0
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
volumes:
- app-data:/app/data
env_file: /srv/app/app.env
mem_limit: 512m
cpus: 1
volumes:
app-data:Check it works
Inspect the running container to confirm each option took effect:
docker ps --filter name=app
docker inspect app --format '{{.HostConfig.RestartPolicy.Name}} {{.HostConfig.Memory}}'The second command should print unless-stopped 536870912, which is 512 MB in bytes.
Troubleshooting
docker: invalid reference format: an option is placed after the image name, or there is a stray space in a-vor-evalue. Keep options before the image and quote values with spaces.Conflict. The container name "/web" is already in use: remove the old container withdocker rm -f webor choose a new name.- Port works locally but not from outside: you bound it to
127.0.0.1, or a provider or host firewall blocks the port. - Environment variable has no effect: the image may read a different name. Check the image's documentation on Docker Hub or its project page.
Next steps
- Turn long commands into files: Docker Compose file explained.
- Choose how containers recover from crashes: Docker restart policies.
