Skip to content

fix: isolate environment variables per container in Docker Compose deployments#8504

Closed
ripgtxgt wants to merge 2 commits intocoollabsio:nextfrom
ripgtxgt:fix/compose-env-isolation-next
Closed

fix: isolate environment variables per container in Docker Compose deployments#8504
ripgtxgt wants to merge 2 commits intocoollabsio:nextfrom
ripgtxgt:fix/compose-env-isolation-next

Conversation

@ripgtxgt
Copy link
Copy Markdown

Changes

Coolify injected a single .env file containing ALL environment variables from every service into every container via an unconditional $service['env_file'] = ['.env']. In a nextjs + postgres + redis stack, Redis could read POSTGRES_PASSWORD and Next.js API keys — a lateral-movement risk.

Now generates a per-service .env.<service_name> file containing only:

  1. Variables explicitly declared in that service's environment: block
  2. Coolify metadata (COOLIFY_*, SERVICE_URL_*, SERVICE_FQDN_*, SERVICE_NAME_*)

The global .env is preserved for backward compatibility.

Issues

Category

  • Bug fix

AI Usage

  • AI is used in the process of creating this PR

Steps to Test

  1. Deploy a multi-service Docker Compose project (e.g. nextjs + postgres + redis)
  2. SSH into the postgres container and verify it cannot read Next.js-specific env vars
  3. SSH into the nextjs container and verify it cannot read POSTGRES_PASSWORD
  4. Confirm all services start correctly with their own variables available

Contributor Agreement

Important

  • I have read and understood the contributor guidelines. If I have failed to follow any guideline, I understand that this PR may be closed without review.
  • I have tested the changes thoroughly and am confident that they will work as expected without issues when the maintainer tests them

kiro-dev28 and others added 2 commits February 21, 2026 01:34
Changes
-------

### PATCH /databases/:uuid — backup configuration support
Adds optional backup_* fields so callers can configure (or auto-create)
a database's backup schedule through the existing resource endpoint,
without needing to know the scheduled_backup_uuid:

  backup_save_s3, backup_frequency, backup_s3_storage_uuid,
  backup_enabled, backup_disable_local_backup,
  backup_databases_to_backup, backup_dump_all,
  backup_retention_{amount,days,max_storage}_{locally,s3}

If no backup config exists for the database, one is automatically
created with sane defaults.  The backup_* prefix avoids any collision
with existing database fields.

### GET /databases — last_successful_backup timestamp
Each backup_config entry now includes a `last_successful_backup`
ISO-8601 timestamp (null when no successful run has occurred yet),
satisfying the issue requirement to surface backup health at a glance
without a second API call.

### POST /databases/:uuid/backups — expose disable_local_backup
Added `disable_local_backup` (boolean) to the create-backup endpoint so
callers can configure S3-only backups from the start.

### PATCH /databases/:uuid/backups/:uuid — fix frequency validation + disable_local_backup
- The frequency field was previously validated with a strict enum
  (every_minute|hourly|daily|weekly|monthly|yearly), preventing custom
  cron expressions.  Validation is now delegated to the same
  `validate_cron_expression()` helper used by the create endpoint.
- Added `disable_local_backup` to the allowed/validated fields.

All other backup endpoints (GET backups, DELETE backup, DELETE execution,
GET executions) were already implemented and are unchanged.

Closes coollabsio#5672
…oses coollabsio#7655)

Problem
-------
Coolify injected a single .env file containing ALL environment variables
from a Compose project into every container via an unconditional

    \$service['env_file'] = ['.env'];

applied to every service.  This meant a compromised Redis container
could read POSTGRES_PASSWORD, and a Next.js container could read
database credentials that are none of its business — a serious
lateral-movement risk in multi-tenant/cloud deployments.

Fix
---
Instead of one shared .env we now generate a per-service env file
(.env.<service_name>) that contains only:

  1. Variables explicitly declared in that service's `environment:`
     block in the Compose file.
  2. Coolify-injected metadata (COOLIFY_*, SERVICE_URL_*,
     SERVICE_FQDN_*, SERVICE_NAME_*) which are legitimate global
     context variables.

Implementation
--------------
* ApplicationDeploymentJob::$composeServiceEnvKeys (new property)
  Populated during compose file generation by parsing each service's
  raw `environment:` block (supports both list and map YAML forms).

* Compose generation loop (line ~643)
  Each service's env_file is set to [".env.<slug>"] instead of [".env"].
  Any env_file entries already present in the original compose are
  preserved after the per-service file.

* ApplicationDeploymentJob::write_per_service_env_files() (new method)
  Called from save_runtime_environment_variables() after the global
  .env is written.  Builds per-service KEY=value lists and writes them
  via the existing remote-command infrastructure.

The global .env is still written (backward-compatible for raw-compose
mode and any tooling that reads it directly).

Scope
-----
Only affects `build_pack === 'dockercompose'` non-raw deployments.
Raw compose mode is intentionally unchanged — users who manage raw
compose files control env_file themselves.
Single-container (dockerfile/nixpacks/etc.) deployments are unaffected.

Closes coollabsio#7655
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants