Skip to the guide

How to run a container website

A container website runs an image an administrator has approved, as the website’s own account. This page is the whole procedure: approve the image, create the website, fill in three fields, and know what the container may and may not do.

Before you start

Two things have to be true.

The server was installed with containers turned on. That is --containers yes at install time. A server without it has no Docker and no container runtime on the create-a-website form, and nothing on this page applies.

An administrator has approved the image. A customer chooses from a list; they cannot name an arbitrary image. Choosing an image is choosing what code runs on the machine, so the list belongs to whoever owns the server.

Step 1, as an administrator

Approve the image.

Go to Server → Container images and press Approve an image. Paste the full reference, including a tag.

traefik/whoami:v1.10
ghcr.io/your-org/your-app:1.4.2
registry.example.com:5000/team/api:2026-10-01
traefik/whoami:v1.10@sha256:0000000000000000000000000000000000000000000000000000000000000000

The server fetches the image’s manifest when you save, so a reference with a typo is refused immediately rather than becoming a website that will not provision. It will also refuse to remove an image while a website is still running it, and names how many.

RuleWhy
A tag is required. nginx is refused; nginx:1.27 is acceptedAn implicit latest means the image you reviewed is not the image that runs tomorrow, which is the one thing this list exists to prevent
A @sha256:… digest may follow the tagPins it exactly, so even a re-pushed tag cannot change what runs
Repository: lower-case letters, digits, . - _ and /, up to 255 charactersThe reference becomes an argument to a command the server runs as root, and the character set alone rules out anything that could become a second argument
Tag: letters, digits, . - _, up to 128 characters, not starting with a dot or a dashSame reason
A registry host may carry a port: registry.example.com:5000/team/api:2.0The last colon is the tag; the earlier one is the port, and both are read correctly
At most 50 approved images, and 300 characters per referenceGenerous for a hosting server, bounded so the list stays something a person can read

A private registry needs the host to be able to pull it. Dashmox does not store registry credentials, so a private image has to be one the Docker daemon on that machine can already fetch.

Step 2

Create the website and fill in three fields.

Create a website as usual and choose Container as the runtime. Three fields decide what runs.

FieldWhat to put
ImageOne of the approved images, chosen from the list. If the one you want is not there, an administrator has to add it first.
PortThe port your application listens on inside the container, 1 to 65535. For traefik/whoami:v1.10 that is 80. This is not a public port and not a port on the host: the server publishes it to loopback only and the web server forwards your domain to it. Get this wrong and the website answers 502.
EnvironmentOne NAME=value per line, exactly as the container receives them. Up to 32 of them.

A worked set of environment lines, for an application that needs a database and a secret:

DATABASE_URL=mysql://hpu_a1b2c3d4:the-password@localhost/hpd_e5f6g7h8?unix_socket=/run/mysqld/mysqld.sock
APP_SECRET=a-long-random-string
LOG_LEVEL=info
TRUSTED_PROXIES=127.0.0.1

Create the database under Databases first and pass its credentials in as environment variables. The server never guesses them, and the panel gives you the whole connection string when it issues the account.

Not localhost or 127.0.0.1 for the database.

A container is on a network of its own, so those addresses are the container rather than the server, and an application given one reports that the database is not running. A container with a hosted database is given the server’s own socket instead: /run/mysqld/mysqld.sock for MySQL or MariaDB, /var/run/postgresql for PostgreSQL. Copy the connection string from the account rather than writing one.

Environment ruleLimit
How many32 variables
NamesCapital letters, digits and underscore, not starting with a digit, up to 64 characters
ValuesUp to 2048 characters, no control characters. A newline would start a second variable
Refused outrightPORT, HOST, PATH, HOME, LD_PRELOAD, LD_LIBRARY_PATH. The server sets these. A website that could change PORT would move its application off the address the web server forwards to and take itself off the air with nothing reporting an error

The server sets HOST=0.0.0.0 and PORT to the port you chose, so an application that reads PORT needs nothing from you. Bind to 0.0.0.0 and not to 127.0.0.1: inside the container, loopback is the container’s own and the web server cannot reach it.

The thing that surprises people

Only /data survives a restart.

Anything a container writes outside /data is lost when it restarts, and saving a change replaces the running container. Uploads, generated files, a SQLite database, a cache you would rather keep: all of it belongs under /data, or it is gone the next time the container starts.

Path inside the containerWhat it is
/appThe website’s own app directory, read-only. Put your code here over SFTP or the file manager. An application that can rewrite its own source turns a small bug into a persistent one
/dataThe website’s private directory, writable, and the only thing that survives. It is above the served directory, so nothing in it is reachable over HTTP
/tmp64 MB of memory, wiped on restart, and nothing in it may be executed
Everywhere elseRead-only, including the image’s own directories

This catches people who have run the same image elsewhere without thinking about it, because a long-lived container hides the problem until something restarts it. Treat the container as disposable and /data as the only floor you can stand on.

What it can and cannot reach

Outbound yes, inbound only through the web server.

DirectionWhat happens
Somebody visiting your domainReaches the web server, which forwards to your port. This is the only way in
Your application calling out: an API, a package registry, an SMTP relayWorks. A container has ordinary outbound access to the internet
Your container reaching another customer’s containerRefused. Every container shares one bridge with container-to-container traffic switched off
Anything on the internet reaching your port directlyRefused. The port is published to the host’s loopback address only
Your container reaching its own hosted databaseOver the server’s socket, which the container is given when the website has a database. Neither engine listens on an address a container could reach
Your container reaching anything else on the hostThrough the bridge gateway, for anything that listens on it. Most of what this server runs listens on the loopback and so is not reachable

So an image that expects to write across its filesystem, install packages at boot, or bind a public port will need adjusting. It runs as the website’s own account with every Linux capability dropped, no ability to gain privileges, and its CPU, memory and process limits handed to Docker from the website’s own limits. That is the trade for running somebody else’s image next to other people’s websites.

An application that needs more than one service

One image, one port, so bring the rest from the panel.

A great many images ship with a docker-compose.yml that runs the application plus a database plus a cache. A container website is one image listening on one port, so a compose file is not something you can hand to Dashmox.

What to do instead: run the application’s own image as the container website, create a database under Databases, turn on Redis if it wants a cache or a lock, and pass the details in as environment variables. Read the compose file to find out which variables the application expects.

A compose file often runs a second copy of the application as a queue worker, and a container website can do that too: Background worker in the website’s Docker section runs the same image with the queue command instead of the web server. An application that queues its work and has nothing consuming the queue serves pages and does none of it, which looks like an application that is working.

If the application cannot be told where its database is, some genuinely cannot, then it is not a fit for a container website here, and saying so now is cheaper than finding out after the certificate is issued.

When it does not work

Four things it almost always is.

What you seeWhat it usually is
502 from your domainThe Port field does not match the port the application listens on, or the application bound to 127.0.0.1 instead of 0.0.0.0
The container starts and immediately stopsIt is trying to write outside /data, or it expects to run as root. Its own output says which; read it below
Everything works, then a restart loses dataThe data was not under /data
The image cannot be chosenIt has not been approved, or it was approved without a tag and refused

Where the output goes. Anything the container writes to its standard output or standard error goes to the server’s own journal, tagged with the website. An administrator reads it with journalctl -t dashmox-<website id> over SSH, and the website’s own logs page carries the web server’s side of the conversation. Docker’s own copy of that output is capped at 10 MB so a chatty container cannot fill the disk.