Set the run command

Last updated

The run command is what your container executes on start. Set it on the app's settings; saving restarts the app.

It replaces the image's command, not its entrypoint

This is the detail that produces the most baffling failures, so it's worth being precise.

Your run command becomes the container's command (CMD). The image's ENTRYPOINT is preserved and still runs, with your command passed to it as arguments.

So for an image whose entrypoint is already the program:

# image: binwiederhier/ntfy   (ENTRYPOINT is "ntfy")

serve            ✅  runs: ntfy serve
ntfy serve       ❌  runs: ntfy ntfy serve

If an existing image fails instantly with an error that looks like a mangled argument list, this is almost always why.

The same applies in reverse to your own images: if your Dockerfile has both an ENTRYPOINT and you also set a run command in the panel, they combine. Pick one — put the full command in the Dockerfile and leave the panel empty, or use a plain CMD and set the command here.

Clearing the run command restores whatever the image specifies.

Use a production server, not a dev server

Generated code and README instructions usually start a development server. They're the wrong thing to deploy:

npm run dev        →   npm start
flask run          →   gunicorn --bind 0.0.0.0:8000 app:app
manage.py runserver →  gunicorn --bind 0.0.0.0:8000 project.wsgi

Dev servers add file watchers and debug output, and several default to binding localhost, which makes them unreachable no matter what port you bind. See Networking.

One process per container

Don't wrap your app in supervisord, PM2, or a shell script juggling background jobs. The platform already restarts your process when it exits, and a supervisor inside the container hides your app's exit code and makes a dead process look healthy.

If you think you need two processes, you usually want either two applications or one program that does both.

Shell form and quoting

The command is parsed into arguments. If you need shell features — pipes, &&, variable expansion — ask for a shell explicitly:

/bin/sh -c "python migrate.py && python app.py"

Note that chaining a migration onto startup means it runs on every restart, including every iteration of a crash loop. Prefer running migrations deliberately.

Running one-off commands

To run something once — a migration, an inspection, a backup — use the app's Shell rather than changing the run command. It executes inside the running container and doesn't affect what happens on the next restart.

The shell works even in a minimal image: a static busybox is mounted into every container, so a scratch or distroless image is still explorable.