Generating .neo.yml from docker-compose.yml
neo config generate migrates a compose project automatically — deterministically now, and it tells you exactly what it could not carry over.
Migrating from compose
neo config generate
Reads docker-compose.yml, identifies the public app service, and writes a .neo.yml with env vars, port, workers and sidecars filled in — a starting draft, not a finished config, and it says so explicitly.
How the app service is chosen
Scoring, highest wins, ties broken by name so the result is identical on every run: a service with build: outranks one without; published ports outrank none; an nginx-proxy style VIRTUAL_HOST/VIRTUAL_PORT counts as a signal when nothing is published. A service whose command is a worker or scheduler (queue:work, schedule:work, horizon, supervisord) is never eligible, regardless of score — it can never become the public app by mistake.
Workers vs. sidecars
A service sharing the app's image or build context, with its own command, becomes a workers: entry — unless its environment conflicts with the app's. Neo workers inherit the app's env verbatim with no per-worker override, so a service that sets DB_QUEUE=study while the app sets DB_QUEUE=nomination can't safely become a worker — mapping it would silently point it at the wrong queue. It stays a sidecar instead, and the output names exactly which variables conflicted.
One-shot services are skipped, not mis-mapped
A service declared restart: "no" — composer install, an asset build, a migrate step — is compose's idiom for "run once and exit." Mapped to a worker it would loop forever; as a sidecar it would exit and look broken. Generate skips these and says where the work belongs instead:
⚠ composer → skipped: runs once and exits — move it to hooks.pre_build
⚠ setup → skipped: runs once and exits — move it to release:
Nothing is dropped without a word
Every compose key the typed parser doesn't act on — healthcheck:, deploy.replicas, Traefik labels: — is listed under "Not carried over," with a pointer to the Neo equivalent when one exists:
Not carried over — review these by hand
~ app healthcheck: add health: to .neo.yml
~ app deploy: replicas map to scale: in .neo.yml
What it can't do
neo deploy builds from a Dockerfile; a compose file with no build: anywhere describes prebuilt images only, and generate says so rather than producing an undeployable config silently. And Neo deploys one public app per .neo.yml — a compose file with several VIRTUAL_HOST-bearing services describes several sites, and generate names which one it picked and that the others need their own project.
Try it yourself — Neo is free, with every feature unlocked.
Get started free