Sokko Docs
Devboxes

Compose stacks

Run your repo's own docker-compose.yml in a devbox — the app plus its databases and queues. Which features work, which are rejected, and why.

If your repo has a docker-compose.yml, a devbox runs that file. Sokko does not translate it into something else and does not ask you to maintain a second copy of it. Your app, its Postgres, its Redis and its worker all come up together, exactly as they do on your laptop.

This is the mode to use when your app needs other services next to it. If it is a single process — a Worker, a Next.js app, a FastAPI service — use a run: command instead. It starts faster and builds nothing. See sokko.devbox.yml.

Getting started

Nothing to configure. A docker-compose.yml or compose.yaml at your repo root is picked up on the first deploy, images are built in the devbox, and volumes start empty — your own migrations and entrypoints seed them.

Two things you may want to say, in a sokko.devbox.yml beside it:

compose_file: deploy/compose.yml # only if it is not at the repo root
expose: web:3000 # which service the preview URL points at

Sokko picks the preview service on its own when exactly one service publishes ports. With several, it stops and asks — expose: is the answer.

Supported keys

Devboxes run compose against an allow-list. Anything not on this list is rejected by name, with the fix in the error. That is deliberate: several perfectly ordinary compose keys let a container read things it should not, and listing the safe ones is the only version of this that stays safe as compose grows.

Top level: services, volumes, networks, version, name, and any x- extension.

Per service:

KeyNotes
imagepulled from any public registry
buildcontext, dockerfile, args, target
command, entrypoint
environment, env_fileenv_file paths must be inside the repo
ports, expose
depends_onstart order is honoured, including condition: service_healthy
healthcheckSokko runs it and waits on the result before starting dependents
restart
working_dir, user
stop_grace_period
volumesnamed volumes, and bind paths inside the repo (./data)
networksbetween your own services

Volume definitions take name and labels. Network definitions take name, labels, internal and driver. Long-form volume entries take type, source, target and read_only.

What gets rejected

RejectedWhy
Absolute or ~ bind mountsA devbox has no host filesystem to share. Use a named volume, or a path inside your repo.
external: true volumes, driver_optsBoth reach outside the devbox.
pid, ipc, userns_mode, network_mode: container:, devices, cgroup_parent, security_opt, privilegedThese weaken the wall between your stack and the machine running it.
Anything else not listed aboveUnknown means rejected, by design.

Service names must start with a letter or digit and contain only letters, digits, _, . and -.

You will see these as COMPOSE_UNSUPPORTED, naming the exact key. Removing it is usually enough — a devbox is a preview environment, and the rejected keys are almost always there for production or for a developer's own machine.

Images and builds

  • Builds happen inside the devbox. There is no registry to push to and no build service to configure.
  • Layers are cached on the devbox's disk, so redeploying the same branch is incremental, and the cache survives an idle stop.
  • Public base images pull over a shared address. A very busy day on Docker Hub can produce REGISTRY_RATE_LIMITED — wait for the window to pass, or use a base image from a registry that does not rate-limit anonymous pulls.
  • Disk fills up over many deploys. Sokko cleans up superseded images after each successful deploy, but a stack with very large images on the smaller plans can still hit DISK_FULL. Destroy and recreate the devbox to get a clean disk.

Databases and data

Every devbox starts with empty volumes. Your compose file's own Postgres, MySQL or Redis comes up fresh, and your migrations and seed scripts run against it the way they do locally.

Sokko does not copy production data into a devbox. If you need realistic data, seed it from your repo — a seed script in the compose stack is the shape that works, because it comes back automatically on the next devbox too.

Credentials committed inside a compose file (POSTGRES_PASSWORD: dev) are fine here: the database is only reachable from inside that devbox, and nothing but your exposed service is reachable from outside. Real secrets — API keys, tokens for third-party services — belong in Repo secrets.

Which service the preview shows

Only one service is served at the preview URL. Sokko picks it in this order:

  1. expose: in sokko.devbox.ymlweb:3000, or just web
  2. the only service that publishes ports:
  3. otherwise the deploy fails with PORT_DETECT_FAILED, listing the candidates

Your other services keep talking to each other by service name inside the devbox, exactly as in compose. They are simply not published to the internet.

The exposed service must bind 0.0.0.0 inside its container. A server on 127.0.0.1 is reachable from nothing but itself, and the deploy will time out waiting for it.

A Dockerfile with no compose file

A repo with a lone Dockerfile and nothing else works too: Sokko builds the image and runs it as a one-service stack. If the Dockerfile declares EXPOSE, that port is used; otherwise PORT is set and the standard port is assumed.

This is the last thing Sokko tries, on purpose — building an image is the slowest way to preview a repo that also has a dev server, and nearly every repo has a Dockerfile for some other reason.

On this page