Skip to main content
All pages in this manual

Running your project

Docker and one-click apps

What this page is for: run an app that ships as Docker containers — your own compose file, or one of the one-click apps: Supabase (Postgres with sign-in, file storage, realtime and the Studio dashboard) and BillionMail (email marketing with its own mail server) — as a website of this server, behind its own domain with HTTPS.

The rule that does not move: SixPanel, your 6amMart projects and every other website keep running directly on the server, never in a container. Docker is an optional component: nothing of it is on the server until you press Install, and it is used only by Docker apps, the one-click apps and the MongoDB and SQL Server engines.

You need

  • Ubuntu 26.04, Ubuntu 24.04 or Debian 13, on an amd64 or arm64 server. On any other system the install is refused and says why.
  • For Docker itself: 1 GB of memory and 4 GB free on the disk. Each app needs more — the app's card says how much before anything is made.
  • On Ubuntu, the universe part of Ubuntu's archive, where its Docker packages live. If your server does not have it, the install says so and gives the one command that adds it (sudo add-apt-repository universe).
  • The owner's login to install or remove Docker, change an app's environment or compose file, show a secret, clean up or restart Docker. A temporary login can start, stop and restart apps, change their settings, and reads their environment with every secret hidden.

Install Docker

Software → Docker → Install. It takes a minute or two.

  • Where it comes from: your system's own packages — docker.io and Docker Compose (docker-compose-v2 on Ubuntu, docker-compose on Debian 13), and docker-buildx where the system has it. Nothing is added from any other source.
  • What SixPanel sets (in /etc/docker/daemon.json):
  • a port a container publishes opens on 127.0.0.1 only unless a file names another address — Docker writes its own firewall rules ahead of every other one, so a port open on every address would be reachable from the internet past Cloudflare and the firewall;
  • container logs are capped (10 MB, three files per container), so a chatty app cannot fill the disk;
  • containers keep running while Docker restarts (live-restore);
  • containers of different apps cannot talk to each other on Docker's default network;
  • Docker's own networks come from a private range that overlaps none of your server's networks. Settings you added to that file yourself are kept as they are.
  • Nothing outside the server reaches a container by its own address. Docker before 28.0 lets a machine on your server's network that routes Docker's private range through it reach a container directly, past the 127.0.0.1 its ports are published on — MongoDB, SQL Server and every app's database included. SixPanel adds a rule to Docker's own DOCKER-USER chain that drops such a connection (a port published on a public address still works), and a drop-in on docker.service puts it back each time Docker starts. Health → Containers reached only where they are published fails when it is gone. If your daemon.json says "allow-direct-routing": true (Docker's own setting for this), that choice is kept, and Health says what it means.
  • Nobody is added to the docker group. Membership of that group is the same as root.
  • Docker was already installed? SixPanel uses it as it is and writes its settings. If it is running containers SixPanel did not start, it is not restarted under them: the page says the new settings apply at the next restart, and Restart Docker does that when you choose. SixPanel never removes a Docker it did not install.
  • Remove: refused while a Docker app or an engine still uses Docker — delete those first. Images and volumes stay on the disk unless you choose to delete them too.

The Docker page

Software → Docker shows the engine (running or not, its version, the Compose version, its settings, disk use) and has these tabs:

  • Containers — every container with its state, image, ports, CPU, memory and who it belongs to (a Docker app, an engine, or nobody). Start, stop, restart, read its logs (Refresh, and how many lines). Remove is offered only for a container that belongs to nobody: an app's container is managed on the app's own page, an engine's on its Software card.
  • Images — each image, its size and which containers use it. Pull an image downloads one by its name (like nginx:1.29-alpine); Update pulls an image's name again. A container made from the older copy keeps running it until it is made again — a Docker app's Update images on its page does that. Remove is offered only for an image no container uses, running or stopped.
  • Volumes, Networks — what Docker holds, and whose each one is.
  • Clean up — first See what it would remove: stopped containers that belong to nobody, images no container uses, networks nobody uses, anonymous volumes and the build cache. Never a named volume (that is somebody's data) and never anything of a Docker app or an engine. Remove these removes exactly that list; if anything changed since you read it, nothing is removed and you are asked to look again.
  • One-click apps — Supabase and BillionMail, each saying whether it can run on this server now and why not.

Docker apps

A Docker app is a website whose app runs in containers. New → Website → Docker app, then pick what to run:

  • Sample app — a tiny web server that shows the request it received, to see a Docker app work end to end. 64 MB at most.
  • Your own compose file — paste the app's docker-compose.yml (and, if it reads variables, its .env), and name the service the domain goes to and the container port it listens on.
  • Supabase and BillionMail — below.

Where it lives. Its folder is /var/www/_sites/<name>/compose/, readable by root only: the compose file, what it mounts, and its .env (its secrets, root 0600). Its containers are named sp-<name>-<service>-1.

How it is reached. The website's domain is served over HTTPS like every website, and the web server passes each request to the app on 127.0.0.1:<port> — one port SixPanel gives the app (between 7100 and 7999). Every other port the app publishes opens on this server only, so a database inside an app is never open to the internet. (BillionMail's mail ports are the one exception — they are the point of a mail server.)

Its memory ceiling. Every container of the app runs inside one limit you set (the whole app together, not each container), so an app can never take the memory your shop and other websites need. Change it on the app's Settings.

What a compose file may not do

Before anything runs, SixPanel asks Docker Compose for the file exactly as Compose reads it (with its .env), checks that, and then runs that checked version — so what was checked is what runs. A file is refused, with a sentence that says what to write instead, when it asks for:

  • a privileged container, the server's own network, processes, shared memory or host name (network_mode: host, pid: host …), the server's devices or GPUs, another container runtime, or a security profile switched off (seccomp=unconfined …);
  • an added capability beyond the few that only touch the container itself (SYS_NICE, IPC_LOCK, SYS_RESOURCE, and the ones Docker gives anyway);
  • a folder of the server mounted into a container — a mount must come from inside the app's own folder. That rules out the Docker socket, which is root on the server;
  • the app's own .env mounted into a container;
  • a network or volume of another app: external: true, or a name: written by hand, or volume driver options (a "local" volume with o=bind is any folder of the server);
  • include:, extends from another file, or an env_file outside the app's folder;
  • a port open on every address: write 127.0.0.1:8080:80 instead of 8080:80, or switch on Publish ports the file gives no address on this server only in Settings.

What every container is given: capped logs, restart unless you stopped it, a process limit and the app's memory ceiling. A fixed container_name is replaced by a name of the app's own — and kept as a network alias, so other services still reach it by that name.

Its page

  • App — each service with its state, health, CPU and memory; Start, Stop, Restart, and Update images (downloads newer images for the tags the compose file names, then starts the app on them). For a one-click app, the addresses and keys it needs, each secret behind Show.
  • Environment — the .env. Secrets show as `** (a password written inside an address, like DATABASE_URL`, too); the owner's Show reads one value and the Activity log records that it was shown. Save and restart** changes the keys you edited and keeps every other line as it was.
  • Compose file — the file itself. Check says what would stop it from running here without changing anything; Save and start checks it again, saves it and starts the app on it. A temporary login and Tia read it with values beside secret names as ****.
  • Logs — what its containers printed, all of them or one service. The app's own secrets are taken out.
  • Settings — the memory ceiling, the largest upload the web server passes on, the service and port the domain goes to, and publishing ports on 127.0.0.1.

A Docker app has no Deploys, Database, PHP, Cron jobs, Files or Terminal page: its compose file is what runs.

Domain, backups and delete

  • Domain & SSL works as for every website. Supabase's own addresses (its public URL and the address its sign-in emails link to) follow the domain on their own.
  • Backups hold the app's folder (compose file, .env, what it mounts), its named volumes, and its database — for Supabase and BillionMail, their Postgres is copied while it is paused for a moment, so the copy is one Postgres can start from. A restore stops the app, puts its files, volumes and database back, and starts it again — even when a step failed half way.
  • Delete stops the app and removes its containers, networks and volumes (its data). Docker must be running for that; the images stay until a Clean up.

Supabase

Needs a server with 4 GB of memory (a "4 GB" server reports about 3,800 MB; below 3,500 MB it is refused with the number), about 1.8 GB free to start, and 8 GB free on the disk.

  • What is installed: Supabase's own self-hosting files at one fixed version (self-hosted 0.8.3, 16 Oct 2026), each file checked against its fingerprint when it is downloaded — a file that differs installs nothing.
  • Every secret is generated for you: the JWT secret, the anon and service_role keys signed with it, the database and Studio passwords and the encryption keys.
  • Studio (the dashboard) is at https://<your domain>/, behind a sign-in: user supabase, the password under Show on the App tab.
  • In your app: the API URL is https://<your domain>; the anon key is for browser code, the service_role key for server code only — never in a browser.
  • Postgres is reachable on this server only — session mode and transaction mode, each on its own 127.0.0.1 port. The connection strings are on the App tab.
  • Sign-up and password emails: when SixPanel Email is installed, Supabase sends through it — set SMTP_USER and SMTP_PASS (a mailbox from the Email page) on the Environment tab. Without it, set SMTP_HOST, SMTP_PORT, SMTP_USER and SMTP_PASS to any mail service.
  • Update images keeps it current within the version it was made from. Moving to a newer Supabase release is not done by SixPanel yet.

Connect a website to Supabase

A Node.js, Python, Java or PHP website gets its Supabase values without typing them: open the website's Environment page, card Supabase.

  • Use a Supabase on this server (the server's owner): pick the Supabase app. The panel writes NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY, SUPABASE_SERVICE_ROLE_KEY and SUPABASE_DB_URL (the database on 127.0.0.1, for migrations) — or the names you type instead. The values are read from the Supabase app every time the website's environment is written, so when that app gets a new key or a domain, the website gets it too and restarts — or is built again, when a NEXT_PUBLIC_ value changed, because those are put into the pages at build time. The website's address is added to the Supabase's SITE_URL (or its list of redirect addresses) so sign-in links come back to it. Until the Supabase app has a domain, its URL is this server's own address: server code reaches it, a visitor's browser does not.
  • Use Supabase Cloud (the owner, or the account whose website it is): paste the project URL, the publishable key and the secret (service_role) key, and optionally the database connection string (Connect → Session pooler). Before anything is saved the panel asks Supabase with them: its Auth must answer with the publishable key, and the secret key must be allowed to list users, which only that project's service_role key is. A key of another project, an expired one, the two keys swapped, or a connection string still saying [YOUR-PASSWORD] are refused.
  • Keys are never shown — every key is **** on the card, and none is written into panel's settings file, a job log, Activity or Tia's answers. A Cloud project's keys are kept in one file only root can read.
  • The service_role key and the database address never take a name starting NEXT_PUBLIC_, VITE_ or another prefix that reaches the browser.
  • The website's Database page shows its Supabase — its size and tables, Manage data in the Database manager, and a link to Studio or the Cloud dashboard. A website can also be made with Supabase from the start: the Create dialog's Database choice has Supabase (this server) and Supabase Cloud.
  • Disconnect stops setting those variables. Nothing in Supabase is deleted.
  • sixpanel.json can ask for the same: "supabase": { "use": "<the Supabase app>" } (see sixpanel.json).

Move from Supabase Cloud to this server

On the same card, Move from Supabase Cloud to this server copies a Cloud project into a new Supabase app here (a copy never writes over a Supabase that already has tables or users):

  • your schemas, their tables and data, views and functions;
  • the signed-up users and their sign-ins (Supabase's auth data), and the storage buckets;
  • the Storage files, when the website is connected to that Cloud project (its secret key reads them) — a file over 50 MB is listed, not copied.

The database is put in as one transaction: if anything fails, the Supabase here is left exactly as it was. Then connect the website to the new Supabase on the same card.

What does not move — do these by hand:

  • Edge Functions (deploy them again into the app's volumes/functions folder);
  • the project's secrets (Edge Function secrets, Vault values encrypted with Cloud's key);
  • pg_cron jobs and database webhooks;
  • Auth settings: sign-in providers and their client secrets, email templates, SMTP;
  • the API keys — this Supabase has its own, and connected websites get them by themselves;
  • roles you created yourself in Cloud: create them here first, or the copy stops at the first object one of them owns.

BillionMail

Email marketing — newsletters, campaigns, contact lists — with its own mail server, webmail and dashboard.

  • One mail server per server: BillionMail is refused while SixPanel Email is installed (and SixPanel Email is refused while BillionMail holds the mail ports), while anything else answers on a mail port (25, 465, 587, 993, 995), or when a BillionMail already runs here. It needs a server with 2 GB of memory and 4 GB free on the disk.
  • The mail host name you give (like mail.example.com) needs an A record to this server that is DNS-only, because mail does not pass through Cloudflare's proxy, and an MX record for your domain. Create the A record yourself: the panel writes no DNS-only record but SixPanel Email's own mail host. The DNS page expects the MX, and nothing in the panel proxies a name an MX of its zone delivers to. Ask your server provider to set the reverse DNS (PTR) of the server's address to the same name, or other mail servers treat your campaigns as spam.
  • Ports open to the internet: 25, 465, 587, 993 and 995. If your provider has a cloud firewall, open them there too. Plain IMAP (143) and POP3 (110) are not published — mail clients use the TLS ports.
  • Its dashboard is at https://<your domain>/<a secret path> — the whole address is under Show on the App tab, with the user (billion) and its password. Webmail is at https://<your domain>/roundcube/.
  • What differs from installing it by hand: BillionMail manages its own containers through a Docker connection SixPanel gives it that reaches its own containers only (restart, logs, commands inside them) — never the server. Its feature for sending from several IP addresses needs the whole server and does not work here. Its built-in fail2ban is off (behind the web server it would see one visitor and ban everyone), and its fixed network range is not used (it belongs to Cloudflare).
  • Its environment is plain words: BillionMail reads its .env itself, so a value is letters, digits and . _ / : @ % + , ~ ^ - only — no spaces, quotes or =.

Tia

Tia can read Docker's state, your Docker apps, their services, logs, environment and compose file — every secret as **** — and start, stop, restart or update an app. Creating a Docker app, changing its environment or compose file, and a clean-up wait for your approval. She never reads a secret's value: to set one, she asks you to type it into a field she cannot see.

When something goes wrong

  • "Docker is not installed on this server" — install it on Software → Docker first.
  • "… are not in this server's package lists" — on Ubuntu, add the universe part of the archive (sudo add-apt-repository universe), then press Install again.
  • A compose file is refused — each line says what to change. Check on the Compose file tab tries again without starting anything.
  • The app did not start — the App tab says which step stopped and why; the Logs tab shows what its containers printed.
  • The website answers "being updated" — the app is stopped, or still starting.
  • It slows down or restarts under load — the App tab shows its memory against its ceiling; raise the ceiling on Settings.

The software card on this page

A Docker app's page shows the Docker card; on Software, Docker says which Docker apps and which MongoDB or SQL Server databases use it. It is the same card as on Software: its state, its version, a waiting update and, for the owner, Restart (asked first, and refused while another job runs). On Software opens its card there, with who else uses it. See Software, search and the system monitor.

Something unclear?Ask Tia