Containers
A site can run your project instead of serving its files: a Node.js app and a MySQL database, declared in one small compose file, with Sitebin in front as the reverse proxy — the site's own address and your custom domains included.
How it works
- Put a
sitebin-container-compose.yamlin the site's root folder. - Upload your app into the folder a service mounts — each root folder of the site is a volume.
- Switch the site to Container on its edit page (or
PUT {"mode":"container"}).
From then on, every change to the compose file restarts the project — however it arrives: the edit page, the in-browser editor, a ZIP, WebDAV, FTP, the API or an agent through MCP.
The compose file
It looks like Docker Compose, cut down to what a site needs:
services:
app:
image: alpine-node-22
environment:
NODE_ENV: production
DB_HOST: db
DB_PORT: 3306
DB_USER: appuser
DB_PASSWORD: apppassword
DB_NAME: appdb
volumes:
- app:/usr/src/app # the site's root folder "app"
http: [3000, 3001] # the ports it speaks HTTP on; each gets an address
egress: allowed # may reach the internet (default: denied)
depends_on: [db]
db:
image: mysql-8.4
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: appdb
MYSQL_USER: appuser
MYSQL_PASSWORD: apppassword
volumes:
- db:/var/lib/mysql
| Key | Meaning |
|---|---|
image | One of Sitebin's own images — never a registry reference:
alpine-node-22 (Node.js 22 on Alpine) or mysql-8.4 (MySQL 8.4 LTS).
Both are pinned to an exact, reviewed version. |
environment | A map or a KEY=value list, as in Compose.
There is no variable interpolation: $HOME stays the five characters $HOME. |
volumes | <folder>:<absolute path>, optionally :ro.
The folder is a root folder of the site — the same one the edit page, WebDAV and FTP show — and is created
if it does not exist yet. |
http | The ports the service speaks HTTP on — one, or a list of up
to 10. Each one gets an address of its own (see Addresses); which domain serves
which port is set on the site, never in this file. A service without http has no address. |
egress | allowed or denied (the default): whether the
service may reach the internet. |
command, working_dir, depends_on | Optional.
depends_on orders the start; it does not wait for the other service to be ready, so connect with a retry. |
Any other key is an error that names it, shown on the edit page — so a
ports: copied from another project tells you it does
nothing instead of silently doing nothing. The key is
http, not Compose's expose: in Docker that
means internal only, and files from elsewhere often carry it on
their database.
Addresses
Every HTTP port gets a generated address. The first
port in the file gets the site's own address,
<id>.sitebin.app; every further port an address of
its own with a random name, as hard to guess as the site's — so an admin
port is not found by reading the app's address. A port keeps its address
as long as the file lists it, and a renamed service keeps the site's
own address.
Custom domains are added on the edit page, through the
API or by an agent over MCP, exactly as for any other site — the same
DNS proof, the same certificates — and each one serves a
port: the one you pick, by default the port of the site's own
address. Every address can serve, redirect to another
of the site's domains with the status code you choose, or — a generated
address, once a domain serves the same port — be switched
off, as described in
Redirects and the generated
address. A redirect is answered before the view password is asked
for, so a visitor of example.com unlocks only
www.example.com.
None of this is in the compose file, so changing an address
never restarts the app. A port that leaves the file takes its
generated address with it; a domain pointed at it stays attached and
answers 404 until you point it at another port — Sitebin
never moves a domain to another service on its own.
Networking
- Services of one site share a private network and reach each other
by service name —
db:3306above. Sites never share a network. - Without
egress: alloweda service cannot reach the internet at all. - Sitebin reverse-proxies each address to the port it serves, with
TLS, WebSockets and the site's view password, if it has one.
/_sitebin/*stays Sitebin's on every site address. - A site in Container mode is never served from its
files: its folders hold the database, and the compose file
holds passwords. A stopped project answers
503; an address serving no port the file declares answers404.
The images
alpine-node-22 runs npm start
in the first mounted folder. If there is no node_modules
yet, it runs npm install first — which needs
egress: allowed; for an offline service, upload
node_modules with the app. With no package.json
it runs node index.js. Set command to run
anything else.
mysql-8.4 is the Docker Official Image mysql, tuned
to fit the container's memory. Mount a folder at
/var/lib/mysql to keep the data — without one, the data
lives in memory and is gone on the next restart.
Limits
- Containers: your plan caps the running services across all your sites — every service in every started project counts. A project that would go over is not started, and the edit page says by how much.
- Memory, CPU, processes: set per container by the instance's operator (on the hosted service, by default: 512 MB, half a CPU, 256 processes). You do not choose them yourself; the operator can change them for one container or for all, and a running container takes the new values without being restarted.
- Storage: everything the containers write counts
against the site's storage. A project that grows past it is stopped —
never deleted; free some space and press Start. The
per-site file count does not apply in Container mode
(
node_modulesalone would break it). - Plan changes: when a plan ends or shrinks, projects over the new cap are stopped, newest first. Their files stay.
Running it
The edit page shows every service with its state, image, volumes and HTTP ports with their addresses, what it uses right now, plus Start, Stop, Restart and each service's recent log. Below it, the custom-domain card lists every generated address and every domain with what it does — the DNS record a domain still needs and Check now included.
Usage, refreshed every 10 seconds: each running service's memory, CPU and processes against its limits, and its public traffic in and out per second — your visitors' requests and its own connections to the internet, not what your services exchange with each other. When several services of a project serve HTTP, the visitors' traffic is shown once for the project.
# switch a site to Container mode
curl -X PUT -H "X-Edit-Password: $PW" -H "Content-Type: application/json" \
-d '{"mode":"container"}' https://app.sitebin.io/api/sites/$EDIT_ID
# start / stop / restart (202; the site document comes back with container.status)
curl -X POST -H "X-Edit-Password: $PW" https://app.sitebin.io/api/sites/$EDIT_ID/containers/restart
# the last lines a service wrote
curl -H "X-Edit-Password: $PW" "https://app.sitebin.io/api/sites/$EDIT_ID/containers/app/logs?tail=200"
# what each running service uses; in_bps / out_bps are BYTES per second,
# null until the second measurement
curl -H "X-Edit-Password: $PW" https://app.sitebin.io/api/sites/$EDIT_ID/containers/stats
# a domain for the second port, and pointing it at another port later
curl -X POST -H "X-Edit-Password: $PW" -H "Content-Type: application/json" \
-d '{"domain":"api.client.com","target":"app:3001"}' https://app.sitebin.io/api/sites/$EDIT_ID/domains
curl -X PUT -H "X-Edit-Password: $PW" -H "Content-Type: application/json" \
-d '{"endpoint":"app:3000"}' https://app.sitebin.io/api/sites/$EDIT_ID/domains/api.client.com/target
An account API token works in place of the edit password, as on every site endpoint. Switching back to Web server stops the project and serves the folders as files again.
Self-hosting
Container sites need the Enterprise edition in tiers mode,
SITEBIN_CONTAINERS=docker, the Docker Engine's socket
(Docker 26 or newer) and a max_containers value on the
tiers that may use them. The full list of settings is in
Configuration, and the security notes
in Operations & security.
services:
sitebin:
image: sitebin:latest-ee
group_add: ["988"] # the gid that owns /var/run/docker.sock on the host
volumes:
- sitebin-data:/data
- /var/run/docker.sock:/var/run/docker.sock
environment:
SITEBIN_ACCOUNT_MODE: tiers
SITEBIN_CONTAINERS: docker
SITEBIN_CONTAINERS_DOCKER_HOST=tcp://…).