Deploy an existing image

Last updated

Run any published image — an official database, a self-hosted tool, or your own from a private registry. This is the fastest path for anything you didn't write.

Create it

Create application → Advanced → Existing Image. You set everything at creation time, which avoids the add-then-restart dance:

  • Image and tag — pin a version rather than latest, so a restart doesn't quietly upgrade you
  • Run command — see the warning below
  • Registry credentials — if it's private
  • Under Advanced: environment variables, volumes, and ports

There's also a Copy share link button, which produces a URL that pre-fills this form. Handy for documenting a setup, or for sharing "deploy this like I did" with someone else.

The run command trips people up

Your run command becomes the container's command. The image's ENTRYPOINT still runs, with your command passed to it as arguments.

For an image whose entrypoint is already the program, the command is only its arguments:

# image: binwiederhier/ntfy   (ENTRYPOINT is "ntfy")
serve          ✅   runs: ntfy serve
ntfy serve     ❌   runs: ntfy ntfy serve

If a third-party image fails instantly with a confusing argument error, this is almost always the cause. Leave the command empty to use whatever the image specifies.

Tell the app its own URL

Most self-hosted tools generate links — for emails, redirects, clone URLs, webhooks — and can't work out their public address by themselves, because TLS is terminated at the edge and the container sees plain HTTP.

So nearly every tool has a setting for this, and it's the most common silent misconfiguration:

Tool Setting
ntfy NTFY_BASE_URL
Gitea GITEA__server__ROOT_URL
Most others Something ending in BASE_URL, ROOT_URL, SITE_URL or PUBLIC_URL

The hostname doesn't exist until the app does, so the sequence is: create the app, read its URL, set the variable, restart.

Persistence and exposure

Two things to decide before you start it:

  • Add a volume for the tool's data directory. Without one, everything it stores is discarded on the next restart. Check the image's documentation for the path — /data and /var/lib/... are common.
  • Every port you bind is public. Many self-hosted tools ship with open registration or no authentication by default, which is fine on a home network and not fine here. Turn registration off, set a password, and do it before the app is first reachable — see Networking.

Sizing

Start small and raise on evidence. Most single-purpose tools are comfortable on a modest allocation; anything with a bundled database or a JVM has a higher floor. An out-of-memory kill shows as exit code 137 in the app's events.