Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

The reliable way to deploy a multi-container Docker Compose application on Coolify is to create a Service inside a project and environment, choose Docker Compose Empty (or an appropriate one-click Service), add your Compose definition, configure secrets, storage, networking and a domain, then deploy and verify every component. Keep the Compose file as the source of truth, expose only the HTTP-facing service, and put databases, uploads and other state on persistent storage before the first deployment.

What Coolify deploys from a Compose file

A Docker Compose file describes one or more containers and the relationships between them: images, environment variables, ports, volumes, networks, health checks and startup dependencies. In Coolify, a Compose stack is represented by a Service resource. Coolify reads the definition, combines it with the variables and settings you provide in the dashboard, and writes generated deployment files on the selected server.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

This model gives you one place to define how the stack should run. The Compose definition should specify service names, image tags, internal ports, health checks, dependencies and mounts. Coolify supplies deployment orchestration, proxy routing, environment-variable management and the server destination.

Think of the Service as a deployment boundary. A deploy normally recreates the containers described by the current definition, so anything that exists only in a container’s writable layer is replaceable. Volumes, bind mounts, file mounts and external data services are outside that disposable layer and should be planned explicitly.

Before you start

  • A Coolify project and environment where the Service will live.
  • A reachable server registered in Coolify, with enough CPU, memory, disk space and Docker capacity for every container in the stack.
  • A domain name for the public web endpoint, with DNS configured to point to the server when you are ready to enable it.
  • Images that the selected server can pull, and credentials for any private registry.
  • A production Compose definition, preferably stored in Git so changes can be reviewed and rolled back.

Decide which component actually serves HTTP traffic. Databases, queues and internal workers normally stay private on the Compose network and should not receive public domains or published ports.

Inventory the stack before opening the dashboard. List each service, its image, required variables, listening port, dependencies, health check, write paths and expected startup order. This catches a common mistake: configuring a domain for a worker or exposing a database simply because its container has a port listed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prepare a production-oriented Compose file

The following pattern contains a web application and PostgreSQL database. Replace the images, commands, ports, credentials and mount destinations with values required by your application. The paths inside a container are image-specific; use the image documentation to identify the real data directory.

services:
  web:
    image: ghcr.io/example/web:1.2.3
    environment:
      DATABASE_URL: postgres://app:${POSTGRES_PASSWORD}@db:5432/app
    depends_on:
      db:
        condition: service_healthy
    expose:
      - "8080"
    volumes:
      - uploads:/var/lib/app/uploads

  db:
    image: postgres:16
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  uploads:
  pgdata:

Define the application service

Use a fixed, deliberate image tag rather than an unqualified latest tag when reproducibility matters. Set the environment variables the application expects. In the example, DATABASE_URL uses db, the Compose service name, rather than a public hostname or container IP.

expose documents an internal port for other containers and the Coolify proxy without publishing that port directly on the server. If the application requires a different listening port, change the value to match the process inside the image. Also check whether the process binds to 0.0.0.0; a process bound only to its container loopback interface may not be reachable by the proxy.

Declare dependencies and health

depends_on expresses startup ordering. The health condition in the example lets the web container wait for PostgreSQL to report healthy. A health check does not repair a failed database or guarantee that an application-level migration has completed, so inspect logs when startup still fails.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a health command that exists in the image and authenticate in the same way the application does. A check that invokes a missing binary, the wrong user or the wrong database can report false failures. Conversely, a simple process check may report healthy while the service is still refusing application connections.

Give stateful paths named volumes

The uploads and pgdata volumes survive container replacement. Container-local files do not: a redeploy can remove the old container and create a new one with an empty writable layer. Add a named volume, bind mount, file mount or external storage service for every database directory, upload directory, media library, generated artifact or other data that must survive replacement.

Mount only the directory that should persist. Mounting over an image directory can hide files shipped in the image, while mounting the wrong directory leaves the real data in the disposable layer. Confirm the path, expected ownership and required permissions in the image documentation before deploying.

Create the Coolify Service

  1. Open the target Project and Environment in Coolify.
  2. Select Create New Resource.
  3. Choose Docker Compose Empty, or select a suitable one-click Service if you want to begin from a packaged template.
  4. Select the destination server and network.
  5. Paste the Compose YAML or load it from the source you selected.
  6. Save the resource before moving through the remaining configuration sections.

Select the server that has the required Docker capacity and the network intended for this workload. If the stack must communicate with another Coolify resource, confirm that the chosen network arrangement supports that connection instead of assuming every resource shares a network automatically.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a one-click Service, the initial Compose definition is saved with the resource. A later catalog refresh does not automatically rewrite an existing Service’s saved definition; review upstream changes and edit the Service explicitly when you intend to adopt them.

Configure environment variables and secrets

Open Configuration → Environment Variables and enter every value required by the Compose file and the application. This includes database passwords, application secrets, signing keys, API credentials and any variables referenced with ${...} syntax.

  • Keep secrets in Coolify rather than committing them to Git.
  • Use the exact variable names expected by the Compose file, including capitalization.
  • Check for accidental whitespace or an empty value when a variable is mandatory.
  • Redeploy after changing a variable so Coolify regenerates the Service’s environment and Compose configuration.

Coolify renders the configured values into the generated .env and deployment files. A value saved in the dashboard but not followed by a deploy or restart will not change already-running containers.

Separate values that are safe to version, such as a non-secret port number, from credentials and signing material. If a variable is used both by Compose interpolation and inside a container, verify both contexts: a value can be present in the generated file yet still be rejected by the application’s own configuration parser.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Configure domains, ports and internal networking

Publish only the HTTP-facing component

Assign the public domain to the web-facing service and specify the internal port on which that container listens. Coolify’s proxy routes requests for the configured domain to that component. The proxy can only reach a process that is listening on the expected interface and port inside the container.

Do not assign a public domain to PostgreSQL, Redis, a queue worker or another internal service unless an external client genuinely requires it and you have designed appropriate authentication and network controls. Keeping those services private reduces accidental exposure and avoids unnecessary host-port conflicts.

Use service names for container-to-container traffic

Services in the same Compose stack can reach one another over the internal network by service name. In the example, the database hostname is db and the PostgreSQL port is 5432. Do not substitute localhost: inside the web container, localhost refers to the web container itself.

Use the internal container port in connection strings even when a host port is published for a special external use. Service names remain stable across container replacement; container IP addresses do not.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose ports deliberately

The port configured in Coolify must match the application’s internal listening port. A published host port is usually unnecessary for the web service because the Coolify proxy provides the external entry point. Publishing a database port expands the attack surface and can create conflicts on the server.

If a service exposes multiple ports, route only the one that serves the domain. Document any additional internal protocol ports for workers or health checks, but do not map them publicly unless the application requires it.

Set up persistence before the first deployment

Review every path where the application writes data and map it to durable storage before selecting Deploy. For a Compose resource, define the mounts in the Compose file; Coolify displays the parsed mounts in the dashboard.

Data type Typical storage choice What to verify
Database files Named volume or external managed database The mount target is the database image’s documented data directory and backups are planned.
User uploads and media Named volume, bind mount, file mount or object storage The application writes to the mounted path rather than another container-local directory.
Generated reports or exports Durable volume or external storage Retention, capacity and permissions match the application’s behavior.
Temporary cache Container-local storage or a disposable volume Users and jobs do not depend on the cache surviving a replacement.

Named volumes are convenient for Docker-managed persistence. Bind mounts are useful when you need a known host path and host-level backup tooling. File mounts can provide specific configuration or data files. An external database or object store can keep critical state outside the application server. Whichever option you choose, document ownership, capacity and backup responsibility.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Adding a mount after data has already disappeared cannot recover the lost files. Confirm the path with the image or application documentation, check ownership and permissions, and test persistence with a controlled restart before relying on the deployment.

Deploy and verify the stack

  1. Select Deploy and watch the deployment output for image-pull, rendering and container-start errors.
  2. Confirm that every expected component is running and that health checks report the intended status.
  3. Open the configured domain and make a representative request, such as signing in, reading a record or uploading a test file.
  4. Inspect web, database and worker logs for connection failures, migrations, permission errors and crash loops.
  5. For stateful services, perform a controlled restart or redeploy and verify that database rows and uploaded files remain present.

Test the path users actually take, not only the landing page. Submit a form, create a database record, enqueue a job and retrieve an uploaded file when those operations are part of the application. This reveals missing worker variables, queue connectivity and write-permission problems that a simple HTTP status will not show.

A green deployment only proves that the deployment process completed. Application-level checks and a persistence test are what establish that the stack is usable.

Update a Compose Service safely

Changes to the Compose definition, image references, environment variables, domains, ports, networks, mounts or component settings do not affect running containers until you deploy or restart the Service.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Action Effect Use it when
Restart Recreates the stack without first pulling newer images. You changed configuration or need to recreate containers using images already available on the server.
Pull Latest Images & Restart Pulls the tags currently specified in the Compose file, then recreates the stack. You intentionally want newer image content for those tags.
Deploy Applies the current Service definition and deployment settings. You changed YAML, variables, domains, storage, networking or another resource setting.

Pin image versions for predictable rollouts, review changes before applying them, and keep a known-good Compose revision so you can restore the previous definition if a new image or configuration fails.

Changing a mount is especially sensitive. Verify the old data location and backup it before switching paths. Changing a database image tag may also require an application-specific upgrade procedure; recreating the container alone is not a substitute for a supported database migration.

Docker Compose Empty, one-click Service or Git-managed definition?

These choices mainly differ in who owns the Compose definition and how updates are introduced.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Approach Definition ownership Best fit Important responsibility
Docker Compose Empty You supply and maintain the YAML in the Service. A custom application or an existing stack you control. Review YAML, secrets, mounts and image updates yourself.
One-click Service Coolify provides an initial template; the existing resource retains its saved definition. Quickly starting a commonly used stack. Check the saved definition and edit it explicitly when adopting template changes.
Git-managed Compose source Your repository is the source of truth. Teams needing review, history and repeatable rollbacks. Keep secrets out of the repository and deploy the intended revision.

A dashboard-edited Compose definition is quick for experimentation, while Git provides review history and an easy way to identify which YAML revision was deployed. A one-click template can shorten initial setup, but it does not remove responsibility for secrets, domains, storage or later updates.

All three approaches still require you to plan persistence, protect secrets, configure the public component and verify the running result.

Backups, recovery and rollback

Persistent storage is not the same as a backup. Keep a recoverable copy of database data, uploaded files and important generated artifacts, and know how to restore them. Test a restore separately from a routine restart so you can distinguish a healthy volume from a usable backup.

For application changes, retain the previous Compose definition and image reference. If a deploy fails, inspect logs first, then restore the known-good definition and deploy it. For stateful migrations, follow the database or application vendor’s rollback guidance instead of deleting volumes or repeatedly recreating containers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common failures

The domain returns an error or timeout

  • Confirm DNS points to the intended server.
  • Check that the domain is assigned to the correct web-facing component.
  • Verify the configured internal port matches the port on which the process listens.
  • Inspect the web container logs and confirm that it is bound to an address reachable from the proxy.

The web service cannot reach the database

  • Use the Compose service name, such as db, as the hostname.
  • Use the database’s internal port rather than a host-published port.
  • Verify both services are attached to the same stack network.
  • Read database health and startup logs for rejected credentials, initialization failures or a still-starting server.

Data disappears after a redeploy

  • Identify the application’s actual write path.
  • Add a named volume, bind mount, file mount or external storage at that path.
  • Check mount ownership and permissions inside the container.
  • Restore from a backup if the old container has already been removed; a newly added mount cannot recreate files that were only in the old writable layer.

Environment values are blank

  • Open Configuration → Environment Variables and fill every required variable.
  • Check spelling and capitalization against the Compose file.
  • Redeploy so the generated environment and Compose files are refreshed.
  • Inspect rendered-container startup logs for the first missing or invalid value.

A template update has no visible effect

An existing one-click Service keeps its saved Compose definition after the catalog changes. Compare the current definition with the desired template, apply the required edits to the Service, save, and deploy.

The image will not pull

Check the image name and tag, confirm the selected server can reach the registry, and provide registry credentials when the image is private. A valid YAML file cannot compensate for an unavailable or unauthorized image.

The stack starts but immediately restarts

Read the service logs and health-check output together. Look for missing variables, invalid commands, permission errors, failed migrations and dependency connection failures. A dependency declared with depends_on may still become unavailable later, so applications should handle reconnects rather than relying only on initial ordering.

Operational checklist

  • The Compose file uses intentional image tags and defines all required services.
  • Health checks exist for dependencies where readiness matters.
  • Only the HTTP-facing service is routed through a public domain.
  • Database, upload and generated-file paths have durable storage.
  • Secrets are configured in Coolify, not committed to Git.
  • Internal connections use Compose service names and internal ports.
  • A deploy or restart follows every configuration change.
  • Logs, health status, domain behavior and persistence have been tested after deployment.
  • Backups and a rollback path exist for stateful data and Compose revisions.

Frequently Asked Questions

How do I add a docker-compose.yml file to Coolify?

Create a Service in the target project and environment, choose Docker Compose Empty, select the server and network, then paste the YAML or load it from your selected source. Save the resource, configure variables, domains and mounts, and deploy.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which Compose service should receive my domain?

Assign the domain to the component that serves HTTP requests, usually the web or reverse-proxy service. Set the internal listening port that Coolify’s proxy should use. Keep databases, queues and workers private unless an external client genuinely requires access.

Can Compose services use each other by container name?

Yes. Services on the same Compose network can connect by service name, such as db, and by the dependency’s internal port. Do not use localhost or a container IP for this purpose.

Why did my data disappear after redeploying?

The files were probably written to a container-local writable layer. Map the application’s real data directory to a named volume, bind mount, file mount or external storage before redeploying. A mount added afterward cannot recover files already removed with the old container.

What is the difference between Restart and Pull Latest Images & Restart?

Restart recreates the stack without first pulling newer images. Pull Latest Images & Restart pulls the tags currently specified in the Compose file and then recreates the stack. Use the latter only when you intend to refresh image content.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why does changing an environment variable not change the app?

Saving a variable updates Coolify’s configuration, but running containers keep their existing environment until a deploy or restart. Check the exact variable name and capitalization, then redeploy.

Do one-click Service templates update automatically?

No. An existing one-click Service retains its saved Compose definition when the catalog changes. Compare the desired template with the saved definition, edit the Service explicitly, save and deploy.

Should I publish my PostgreSQL or Redis port?

Usually not. Keep databases and queues on the internal Compose network and connect by service name. Publish a port only for a genuine external-client requirement, with appropriate authentication and network controls.

The Bottom Line

Deploy a Compose application on Coolify as a Service: define the complete stack in Compose, supply secrets through Coolify, route one HTTP-facing component through a domain, and put every important write path on durable storage. After each YAML, variable, image, network, domain or mount change, deploy or restart and verify logs, health, real application requests and persistence. That workflow keeps container replacement routine instead of destructive.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.