🐳 docker-compose.yml Generator
Build a complete docker-compose.yml from a simple form. Add multiple services with ports, volumes, environment, depends_on, networks, plus top-level volumes and secrets.
🔒 About Privacy
- ・All inputs are processed entirely in your browser
- ・No server upload, no storage logs, no database
Compose file version pinned to 3.9 (modern)
🌐 Top-level definitions
📄 docker-compose.yml
📖 Where people get stuck
Specify services, ports, volumes, environment variables, dependencies, networks and secrets in the form, and this assembles a docker-compose.yml you can use as-is. Everything runs in the browser and nothing is transmitted. What this tool guarantees is the YAML syntax and structure, and nothing more — it cannot verify that an image exists, that the application inside it boots, or that your services actually reach each other. Most failures of docker compose up are not about YAML formatting but about three things beyond it: ordering, routing and permissions. Those three are what follows.
| Case | What happens | What to do |
|---|---|---|
depends_on only waits for started, not for ready |
All depends_on guarantees is the start order of containers; it does not look at whether the process inside is ready to accept connections. A database takes seconds to tens of seconds after starting to finish initialization and recovery, so your application connects to a port that is not listening yet and dies. The symptom is distinctive: only the first up fails, and running up again works, because by the second attempt the database is already warm. That intermittency is why it slips through locally and first appears in CI. It is not an unreproducible bug; it is a race condition. |
Give the dependency a healthcheck and have the dependent side specify condition: service_healthy. For PostgreSQL, test: ["CMD-SHELL", "pg_isready -U postgres"] with interval: 5s, retries: 10 and start_period: 10s is a practical starting point. Even that is not complete — if the database restarts while everything is running, the application loses its connection again. The real answer is connection retry inside the application. A design that does not depend on start order behaves identically under compose, under Kubernetes and against a managed database. Treat the healthcheck as a convenience that improves the development experience. |
Getting the two sides of ports and volumes backwards |
In both, the left side is the host and the right side is the container ("8080:80" maps host 8080 to container 80). Three accidents grow from here. First, ports publishes on all interfaces by default — write "3306:3306" and anyone on the same LAN can reach your database. Second, services on the same compose network can talk to each other by service name without publishing any port at all; db:5432 resolves, so you generally do not need ports for your own application. Third, a bind mount hides what the image contains — writing ./:/app means the /app/node_modules the image built is no longer visible, and you get module-not-found errors that occur only under compose. |
Put only what genuinely needs to be reachable from outside into ports, and leave everything else out. If you want to connect to the DB from your machine, write "127.0.0.1:3306:3306" to confine it to loopback. The standard fix for the node_modules problem is to overlay an anonymous volume: list two lines under volumes:, ./:/app and /app/node_modules — the latter layers on top of the bind mount so the image contents survive. Permission mismatches (host UID differing from container UID, so writes fail or root-owned files appear) can be aligned with user: "${UID}:${GID}", but this only bites on Linux — Docker Desktop on macOS and Windows absorbs it in the virtualization layer, so it will not reproduce there. |
| Losing track of where an environment variable came from | Compose has three similarly named mechanisms that do different things, and that is the source of the confusion. (1) environment: passes values into the container. (2) env_file: does the same thing from a file. (3) ${VAR} inside the YAML is a variable compose itself substitutes while reading the file, and its value can only come from the host shell or the .env at the project root. That is the biggest trap — values written in env_file: are not used to resolve ${VAR}. And since an undefined ${VAR} quietly becomes an empty string rather than an error, image: myapp:${TAG} silently turns into myapp: and pulls latest. |
Do not guess — read docker compose config. It prints the final YAML with every substitution resolved, and it is the only place you can see what is actually being passed. Any value that comes out empty is an undefined ${VAR}. Decide on a policy for secrets too: a value written directly into environment: is readable by anyone through docker inspect and ends up in Git. env_file: is better in that the file can be listed in .gitignore, but inside the container it is still an ordinary environment variable. In production, use the compose secrets: mechanism or a secret management service — the former is mounted as a file, so it is not inherited automatically by child processes the way an environment variable is. |
The version: '3.9' this tool emits is skipped by the current Compose V2. You will see the warning the attribute version is obsolete, but it has no effect on behavior — delete that line if it bothers you. The version number existed in the Compose V1 era to select a feature set; V2 always interprets the file against the latest specification. Two more operational traps worth naming. restart: always will restart a container that dies immediately, forever — the logs fill with repetitions and the real error message survives only in the first iteration, which makes it hard to find. Remove it while you are investigating. And docker compose down does not delete named volumes. When a database initialization script "does not work", an old volume is usually still there, because most images only run initialization when the data directory is empty. Recreating it requires docker compose down -v, and that command really does destroy the data.
📖 How to Use
-
1
Choose a preset or add a serviceStart from one of six presets (LAMP, Next.js, Laravel, etc.) or add a blank service card.
-
2
Fill in service fieldsEnter image, ports, volumes, environment, depends_on, restart, and networks. Output updates in real time.
-
3
Copy or downloadOne-click copy the generated YAML, or download as docker-compose.yml.
❓ Frequently Asked Questions
Which compose file version is generated?
Are passwords and API keys uploaded?
Can the generated YAML be used directly with docker compose up?
🔗 Related Tools
🐛 Found a bug or issue with this tool?
Free to use, no signup. Even just the steps to reproduce are helpful. Reports go directly to the operator and help us fix issues.
Thanks for your report!
Your report has been delivered to the operator and will be used to improve the tool.