A Docker image of yours now runs all the time beside your SnoutData Cloud project, at its own address, with your database a connection away. It is for the one kind of code a request cannot hold: a process that keeps a connection of its own open.
snoutdata containers deploy whatsapp ghcr.io/acme/wa-bridge:1.4
whatsapp running
https://<ref>.api.snoutdata.com/containers/v1/whatsapp/
Listens on PORT (8080). DATABASE_URL and your secrets are in its environment.
Anything written inside the container is lost when it restarts or is redeployed.
Keep what matters in your database.That is a WhatsApp client, an MQTT or Kafka subscriber, a mail inbox watched with IMAP idle or a game loop, running beside its data with nothing for you to host.
Most backends do not need one
Before reaching for a container, it is worth knowing what already needs no server at all. A Socket.IO backend is usually SQL functions that answer events. An API endpoint, a webhook or a scheduled job is a function. Those start when they are called and cost nothing in between.
What is left is code that has to stay connected to something else for as long as it runs: a client logged in to a messaging network, a broker subscription, a market data feed. That code needs a process that never stops, and until now it meant keeping a server of your own beside your project. A container is that process, run for you.
What happens when you deploy
- The tag is pinned. The deploy asks your registry what the tag points at now and keeps that digest. What runs is that exact image until you deploy again, whatever happens to the tag. Any registry works: Docker Hub, GitHub, GitLab or your own, with a token for a private one, read from stdin so it is in neither your shell history nor the process list.
- It must include arm64. SnoutData runs on arm64. An image built for amd64 only is refused at deploy with the command that builds both, rather than failing later on a machine where nobody reads why.
- It knows its project.
DATABASE_URL,SNOUTDATA_URLand the project's keys, every secret you set withsnoutdata secrets set, andPORT, the port it should listen on. - Its address needs no key.
/containers/v1/<name>/reaches your container directly, websockets included, because the callers it exists for (a webhook sender, your own front end) cannot add a SnoutData key. It authenticates its own callers, the way any server you run does. - It is kept running. A container that exits is started again from its image, waiting a little longer each time, and
snoutdata containers listsays why it stopped, with the last lines it printed.logsshows what it wrote in the last seven days.
It is stateless, on purpose
Every deploy, restart or move starts a container from its image again, so anything it writes inside itself is gone. Its disk is for scratch: caches, temporary files. What matters goes in your database, which is why DATABASE_URL is already set, or in Storage for files.
For a WhatsApp client that means its session lives in a table rather than a folder, so a restart reconnects without scanning a QR code again. Most libraries of that kind take a custom store for exactly this. Your data then sits where your backups, your policies and the rest of your app already are.
Your image, in a box of its own
A container runs code we have never seen beside other people's projects, so it is walled in several ways at once:
- gVisor. Its system calls are answered by gVisor's own kernel in user space, not by the machine's.
- A user of its own. Inside it may be root; outside it is an unprivileged user that owns nothing else.
- Limits. Its memory and CPU are capped. Over its memory it is stopped and started again; it cannot take its neighbours down with it.
- A network per project. Each project's containers share a small network of their own. The public internet is reachable, your database through its front door like any client, and nothing private: not the machine, not the cloud's metadata service, not another project's containers.
- Limits on what it sends. It may open 20 connections a second and hold 1,000 open, send 2 MB a second on them, and may not send mail on port 25. We count what each container sent and was refused, and an hour that looks like it is doing something to others is flagged to its owner and to us.
What the live checks found
Before calling it done we deployed a real image to a real paid project, through the same door your app uses, and looked at the machine it landed on. It found two things our own tests had not, both fixed before this post:
- The memory and CPU limits were set but not applied. Started from the service that runs the fleet, the sandbox landed outside the group that carried its limits. It now starts inside it, and a container that asks for more memory than its plan is stopped.
- A container that crashed could not be started again: the sandbox's settings were not kept for a restart. It is now made again from its image, which is what a restart should mean for a stateless container anyway. Killed on purpose, it was back in about two seconds.
The same check then put a probe on a second project's network: it could not reach the container, while a probe on the container's own project network could.
Where it is today
Live on SnoutData Cloud for projects on the Plus plan (one container of 256 MB) and the Pro plan (three of 512 MB each), half a CPU core each. A free project cannot run one, because a container runs all the time.
What a project's containers send to the internet is included up to 5 GB a month on Plus and 60 GB on Pro. There is no charge past it: at 80% you get an email, and at the allowance the containers stop until the 1st of the month, then start again on their own.
Not yet: images are pulled from your registry on each machine they start on, rather than copied to ours at deploy; one image per container (no compose files); one copy of each container; and no volumes, which the stateless rule makes deliberate.
The runner is our own and open source, Apache-2.0, at github.com/snoutdata/snout-containers, and in the self-hosted stack as an optional profile for a machine with gVisor and podman.
Start with the Containers guide, which has every limit and the environment a container gets.