1796 Commits
Author SHA1 Message Date
Andras Bacsai 81045b2dc2 fix(compose): deploy large Compose files such as Sentry self-hosted
Sentry self-hosted (getsentry/self-hosted 26.8.0) could not be deployed
as a Docker Compose application. Three separate problems blocked it:

- YAML collection aliases: the first fix raised the limit to 256 only in
  Application::oldRawParser(). The safety check added in v4.4.0
  (validateDockerComposeForInjection) and all other Compose parse calls
  still used the Symfony default of 128, so the Sentry file (133 aliases)
  failed with "Maximum number of collection aliases (128) exceeded".
  Add parseDockerComposeYaml() and use it for every Compose parse.
- Argument list too long: the deployment job sent the generated Compose
  file as base64 inside the SSH command. Linux limits one argument to
  128 KiB, and the Sentry file is about 370 KB. execute_remote_command()
  now accepts an 'input' key that streams content over SSH stdin, and
  the job writes the Compose file with it.
- A list-style build arg without a value (build.args: [SENTRY_IMAGE])
  merged into a map environment kept its numeric key, so Docker Compose
  rejected the file with "non-string key in ...environment: 0". It now
  becomes NAME with no value, as in Compose.

Closes #11725
2026-10-07 13:30:42 +02:00
Andras Bacsai 575bfaa085 feat(analytics): add configurable client IP storage modes
Allow full, anonymized, or disabled IP recording per server through the UI
and API, and pass the selected mode to Sentinel. Adapt traffic breakdowns
to the effective IP mode, including views that aggregate multiple servers.
2026-10-07 12:35:22 +02:00
Andras Bacsai 407c384b09 feat(backups): add configurable missing volume backup alerts
Check enabled volume backup schedules hourly and notify teams when no
execution occurs within the configured number of days. Expose the alert
threshold in the UI and API, with alerts disabled by default.

Route volume backup results and warnings to the appropriate Telegram
backup threads.
2026-10-07 09:37:35 +02:00
Andras Bacsai be72005980 fix(deployments): expire missing Horizon jobs after deployment timeout
Keep reserved jobs active and bound missing Horizon records by the server's
 deployment timeout. Add regression coverage for active and finished jobs.

Use UTC PostgreSQL sessions and bump the release version to 4.4.1.
2026-10-06 21:51:58 +02:00
Andras Bacsai 9e20084302 fix(auth): build email verification links from the instance URL 2026-10-06 12:56:57 +02:00
Andras Bacsai 5e92a7267c fix(cleanup): prune audit events in batches and delete old finished runner executions 2026-10-06 12:56:57 +02:00
Andras Bacsai ecf969e35d fix(audit): remove credentials from URLs before storing audit changes 2026-10-06 12:56:57 +02:00
Andras Bacsai 116806df1b fix(compose): allow content files outside the resource directory again
Since aabd12737 a Compose bind volume with a content block had to stay
inside the resource directory. Services and Compose applications saved
in v4.3.23 with other paths, and Git Compose files with them, no longer
started or deployed.

Only admins and owners can edit Compose files, and they can already
write any host file with a plain bind mount, so the rule added no
security.

- Write content files to their host path again, as in v4.3.23
- Keep shell escaping and the safe-character checks on volume sources,
  so injection attempts are still rejected
2026-10-06 11:03:39 +02:00
Andras Bacsai 86d84e21c0 perf(databases): load Redis-family passwords once for database lists
Connection URLs of KeyDB, Dragonfly and Redis queried their password
variables once per database, so the database list API ran extra queries
for every database.

- Read REDIS_PASSWORD and REDIS_USERNAME from the loaded relation
- Load the variables once per type in the database list and the
  environment details API
2026-10-06 06:59:22 +02:00
Andras Bacsai 3f62f4d80a fix(server): fail the queued deployments of a deleted server
Queued deployments of a deleted server could never start and stayed in
the queue. Deleting a server now marks them failed with the log entry
"The server was deleted."
2026-10-05 23:10:52 +02:00
Andras Bacsai 7f12e3b513 fix(databases): keep the server password of existing Redis databases
v4.3.23 started Redis with the stored REDIS_PASSWORD value as it is and
unquoted, and the connection URLs used the same value. 4.4.0 resolved a
shared variable reference for the start command only, so after the first
restart the server password changed and no longer matched the URLs.

- Add legacy_password_quoting to standalone_redis; existing databases
  keep the v4.3.23 server password
- New databases resolve REDIS_PASSWORD (local, shared or remote secret)
  and quote it in the start command
- Connection URLs use the same password as the server
- Hide the flag in the API, as for KeyDB and Dragonfly
2026-10-05 22:49:36 +02:00
Andras Bacsai b08383805c fix(sentinel): prevent concurrent starts and duplicate restarts (#12100) 2026-10-05 21:56:17 +02:00
Andras Bacsai 152e06cf43 fix(databases): let REDIS_PASSWORD override the KeyDB and Dragonfly password
The stored password is the default. Before, a local REDIS_PASSWORD was
rewritten to the stored password, and a shared one replaced the stored
password, so the old password was lost.

- A local, shared, or remote secret REDIS_PASSWORD sets the server
  password; the stored password and the variable are not changed
- Removing the variable, or an empty value, uses the stored password
- Connection URLs show the password in use; for a remote secret they
  show the reference, not the secret
- Databases created before this release keep the v4.3.23 behavior
2026-10-05 21:25:57 +02:00
Andras Bacsai 8aa57d66e8 fix(previews): give each preview its own volume for external volumes
Previews of Docker Compose applications used external volumes as
written, so a preview mounted the production volume. Two database
containers on the same data can corrupt it.

- A preview uses {uuid}_<name>-pr-N with a name-only declaration, the
  same name as v4.3.23, so old preview volumes are used again
- Production still uses the external volume as written
- Preview cleanup removes the preview volume, never the external one
- Do not show the external volume warning for previews
2026-10-05 21:25:40 +02:00
Andras Bacsai 2361161487 fix(service): write large compose files through SSH stdin
Service compose and .env content was sent inline in the SSH command.
Linux limits one argument to 128 KiB, so a service with a generated
compose over about 96 KB failed to save or start with "Argument list
too long".

- Add instant_remote_write_file(), which streams content to a remote
  tee over SSH stdin (sudo tee for non-root SSH users)
- Add SshMultiplexingHelper::generateSshStdinCommand() with the same
  SSH options as generateSshCommand()
- Write the service compose and .env with the new helper
2026-10-05 17:04:05 +02:00
Andras Bacsai 88efc0716e fix: enforce resource team isolation and preserve ClickHouse data
Scope resource permissions and related records to the owning team across
team switches, cloning, deployments, and resource operations.

Merge ClickHouse volume mounts, preserve anonymous data volumes on stop,
and require adopting current data before starting or restarting.

Fix file storage deletion checks and add regression coverage.
2026-10-05 14:23:04 +02:00
Andras Bacsai 4227ec6ebf fix(storage): require deploy permission for external host writes
Require deploy or root token access when creating managed mounts outside
resource directories or changing their file contents through the API.

Synchronize KeyDB and Dragonfly passwords with local and shared
REDIS_PASSWORD values while preserving secret manager resolution.
2026-10-05 12:03:37 +02:00
Andras Bacsai 554bd354bf fix: support host storage paths and clean up cancelled deployments
Allow validated host file and directory mounts outside resource directories
while protecting external paths from deletion.

Clean up cancelled deployment containers and clear tracked process IDs.
Preserve legacy KeyDB and Dragonfly password parsing, accept server backup
paths without extension restrictions, and allow Compose variable proxy ports.
2026-10-05 11:08:52 +02:00
Andras Bacsai b235a480b2 fix(service): detect registry-prefixed Grafana server images (#10617) 2026-10-04 13:39:19 +02:00
Andras Bacsai 9850793109 Merge remote-tracking branch 'origin/main' into pr-11788-encrypted-audit-change-details 2026-10-04 13:13:16 +02:00
Andras Bacsai 4c2253c2ad fix(previews): restrict PR loading and status updates to GitHub Apps
Add Application::isGithubAppSource(), which checks that the source is a
GithubApp, and use it in place of is_github_based() for pull request
status updates in ApplicationDeploymentJob and for the "Load pull
requests" action in the previews view.

Previews::load_prs now throws an error for non-GitHub App sources
instead of calling the GitHub API with a GitLab or other source. Also
adds a test checking that GitLab sources don't show or load GitHub pull
requests.
2026-10-04 12:29:48 +02:00
Andras Bacsai 5805a28872 fix(audit): redact raw credential fields and keep hidden fields excluded
Raw commands, URLs and configuration blobs can contain credentials even
when their field names don't look sensitive. Add a list of these fields
to AuditEvent (git_full_url, build/start commands, database confs,
proxy configuration, compose files, environment variables, etc.). Expose
AuditEvent::isSensitiveField() so redaction and audit change capture use
the same check.

Auditable now excludes a model's class-level $hidden fields as well as
its runtime hidden fields. A field stays out of audit changes even after
makeVisible() is called on the instance.

Add regression tests for credentials embedded in raw fields, database
config on create/delete, nested metadata and proxy configuration, and
hidden fields after makeVisible().
2026-10-04 12:26:37 +02:00
Andras Bacsai 4759b7bcf7 fix(audit): redact object values and isolate change preparation failures 2026-10-04 12:26:06 +02:00
Andras Bacsai 83d19800a6 feat(audit): store encrypted field-level changes on mutations
Record old/new values for auditable create, update, and delete actions, persist them encrypted on audit events, expose them in the API, and let the team audit log expand a field-level before/after view. Sensitive, hidden, and identifier fields stay out of the change payload.
2026-10-04 12:26:00 +02:00
Andras Bacsai 9f7697d501 feat(cleanup): purge stale unsubscribed teams (#11668) 2026-10-04 12:13:41 +02:00
Andras Bacsai 66cc64e0d9 fix(sentinel): repair stalled Sentinel and show why pushes fail
- Restart a running Sentinel that has not pushed for 10 minutes, at most
  once per hour. The restart applies the current URL and token.
- Restart an unhealthy Sentinel. Do not restart while its health check
  starts or while its API does not answer yet, which caused restart loops.
- Remember why pushes fail: the reason Coolify rejects a push, and the last
  push error from Sentinel's /api/push-status. Show it on the Sentinel page
  while Sentinel is out of sync.
- When the instance URL or public IP changes, move remote servers that use
  the generated Sentinel URL to the new URL. Custom URLs stay.
2026-10-04 12:03:54 +02:00
Andras Bacsai c2f63c8f10 fix(server): remove redundant SSH work from server checks
- ServerCheckJob uses the stored reachability state instead of a second SSH
  probe. One failed probe no longer marks the server unreachable without
  UNREACHABLE_THRESHOLD or marks all its resources exited.
- Do not dispatch ServerCheckJob for build servers and Swarm workers.
- Check cloud provider state every 5 minutes instead of every minute.
- ServerPatchCheckJob uses isFunctional() and drops the unused retries.
- Delete the unused ServerCleanupMux job.
- Disable SSH multiplexing per command. The global config change stayed in
  the queue worker, so later jobs (also deployments) ran without mux.
2026-10-04 00:51:58 +02:00
Andras Bacsai 9eb666e982 fix(proxy): connect coolify-proxy to destination network before compose up
Add StandaloneDocker::proxyConnectCommand() and run it inline when a
destination is created, replacing the synchronous
ConnectProxyToNetworksJob dispatch. Deployments also run it before
docker compose up, so the proxy is attached to the destination network
even when it was missing.
2026-10-04 00:35:21 +02:00
Andras Bacsai c3b32322eb fix(volume-backups): back off failed container and S3 recovery
Recovery attempts now back off exponentially, stop after 8 attempts, and
mark the execution as needing attention with a failure category. The
scheduler dispatches at most 100 recovery jobs per run, and a pending S3
cleanup no longer blocks new backups. S3 delete errors now raise with
their cause.
2026-10-03 21:25:11 +02:00
Andras Bacsai a7cc32f249 fix(api): hide the legacy password quoting flag of KeyDB and Dragonfly
legacy_password_quoting is internal start-command state. Add it to the
hidden fields of both models, so the database API does not return it.
2026-10-02 17:03:55 +02:00
Andras Bacsai 5b1fcd6e13 feat(auth): show OAuth-only registration notice on login page
Move the user creation check from OauthLoginService into
OauthSetting::allowsUserCreation(), so the login view can use it.
The login view now gets a can_register_with_oauth flag. The flag is
true when an enabled OAuth provider allows creating new users. The
view uses it to show the new auth.register_with_oauth message.
Add translations for the message in all locales, plus test coverage.
2026-10-02 16:45:33 +02:00
Andras BacsaiandClaude Opus 5.5 9862f75dc2 fix(audit): reduce audit log noise and keep instance events out of team logs
- Ignore status, heartbeat, and check fields that background checks write
  during a user request.
- Record repeated unauthenticated failures (failed logins, API auth,
  webhook signatures) once per source in a short window.
- An explicit null team_id in the context is kept, so instance events are
  not stored on the admin's current team.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:10 +02:00
Andras BacsaiandClaude Opus 5.5 aa2592b17c fix(security): stop leaking locked shared variables and proxy secrets
- References to locked (shown once) shared variables keep their reference
  text in the UI, copy action, and API instead of the locked value.
- Remove the saved proxy configuration and validation logs from nested
  servers (for example destination.server) in API responses unless the
  token can read sensitive data.
- The services list API now applies removeSensitiveData to every service.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:00 +02:00
Andras BacsaiandClaude Opus 5.5 a5ec854e35 fix(services): resolve the old denoKV template key to deno-kv
A renamed template keeps its old key as an alias, so API callers and
services stored with the old key still find the template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:52 +02:00
Andras BacsaiandClaude Opus 5.5 9978929779 fix(databases): keep the server password of existing KeyDB and Dragonfly databases
The start command now quotes the password. Databases created before this
release keep the unquoted v4.3.23 argument when quoting would change the
password the server receives, until their password changes. Remote secret
passwords are always quoted exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:41 +02:00
Andras BacsaiandClaude Opus 5.5 9bdbbeef7a fix(secret-manager): use remote secret values exactly and check them before a restart
- Write remote secret values as exact dotenv literals in the service .env
  file and escape "$" where they go directly into a generated compose file
  (environment, start commands, healthchecks).
- Resolve remote secrets before a service or database restart stops the
  running resource, and show the secret manager error instead of a generic
  start failure (new RemoteSecretException).
- Cache fetched key names for the autocomplete and rate limit how often a
  user can contact the secret manager.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:41 +02:00
Andras Bacsai 51e6949c36 fix(sentinel): stop hourly check job pile-up and self-heal Sentinel
- Start Sentinel with --restart unless-stopped so Docker restarts it
  after a crash or reboot.
- Make CheckAndStartSentinelJob unique per server so copies cannot
  collect in a slow queue.
- Read X-Sentinel-Version on push and update an outdated Sentinel
  without SSH; skip the hourly SSH version check for servers that
  report their version.
2026-10-02 09:41:04 +02:00
Andras Bacsai 50be16be8f revert(scheduler): go back to the cron scan and keep the reliability fixes
The missed daily backups on Coolify Cloud came from Redis evicting queue keys
(allkeys-lfu on a full instance), not from the scan of all schedules. The
next_run_at design (452259aac, 5c5740533) did not prevent that, and it added
stored state that must be kept in sync on every frequency, timezone, and server
change. It was not released, so remove it:
- revert next_run_at, its model hooks, the "Next run" UI, and the diagnostics
  changes; restore scheduled_job_states and the queue overlap lock
- a new migration removes the next_run_at columns and creates
  scheduled_job_states again where the removed migration already ran

Keep the changes that fix real problems:
- run the manager every minute from the scheduler with `scheduled:dispatch`,
  so a busy queue cannot delay it past the catch-up window
- load the server chain of scheduled backups with each chunk, which removes
  about 7,000 queries per run on Coolify Cloud
- catch Throwable for each schedule, so one broken schedule does not stop the
  others
- publish database and volume backups again when their queued job was not
  started after 60 minutes (for example, lost in Redis); log lost tasks and
  Docker cleanups as missed. A lost backup is not run again when a newer
  occurrence of the same schedule exists.
2026-10-02 08:05:18 +02:00
Andras Bacsai b5393e24ec fix(server): recheck unreachable servers before failing deployments
Deployments failed with "Server is not functional" when the server was
still flagged unreachable after a single failed scheduled check. Add
Server::isFunctionalAfterRecheck(), which runs a live SSH and Docker
connection check when the server is marked unreachable. It skips the
check for disabled servers and servers with placeholder IPs, then
re-evaluates isFunctional(). ApplicationDeploymentJob now uses it.

ServerManagerJob no longer skips the SSH connection check for servers
with a live Sentinel when the server is flagged unreachable or unusable.
The Sentinel heartbeat does not restore these flags, so without the
check they would never recover.

Add feature tests for the recheck behavior and unit tests covering the
Sentinel skip condition.
2026-10-01 09:30:21 +02:00
Andras BacsaiandClaude Opus 5.5 69c809f4a2 fix(server): stop marking servers offline after one failed check
A single failed SSH attempt or any unrelated exception in
ServerConnectionCheckJob marked the server unreachable and unusable, so
deployments failed with "Server is not functional" while the server was
online.

- Retry a failed SSH attempt once after 3 seconds within the same check.
- Mark the server unreachable only after UNREACHABLE_THRESHOLD (2)
  consecutive failed checks; notifications use the same threshold.
- Keep the stored state when the job fails for a reason other than SSH or
  Docker (database, cache, bugs).
- Raise the job timeout to 60 seconds and the overlap lock above it. The
  old 15 second timeout killed checks before they wrote a result.
- Run the Docker and Compose version commands once with a 15 second
  timeout. A failed Compose check no longer marks the server unusable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:30:19 +02:00
Andras Bacsai bcc07dd47e feat(github-runners): allow public repositories and refuse pull request jobs by default
The runner group was limited to private repositories, so jobs from
public repositories never got a runner: Coolify still started a runner
for each matching job, which then waited idle until its timeout.

The runner group now allows every repository of the organization,
public ones too; existing private groups are updated. GitHub gives a
runner any queued job with matching labels, so pull request jobs are
refused inside the runner: a job-started hook fails jobs from
pull_request, pull_request_target and review events before any step
runs, and fails closed when the event is unknown. A new runner setting,
off by default, allows pull request jobs; the settings page then warns
to require approval for fork pull request workflows on GitHub.
2026-10-01 08:14:30 +02:00
Andras Bacsai 8d7469230c fix(secrets): skip remote secret names that are not valid variable keys
Import all stopped at the first remote secret whose name is not a valid
environment variable key (for example db-password in Vault or
Infisical). The key setter threw, so the import was partial, no audit
entry was written, the variable list did not refresh, and the user saw
only a generic error.

Invalid names are now skipped and listed in the result toast and the
audit entry, and the rest are imported. Browse marks invalid names and
offers no add action for them, and adding one shows a clear error. The
refreshEnvs event now goes only to the environment variable list.
2026-10-01 07:16:45 +02:00
Andras Bacsai 8144b1cbc1 fix(transfer): keep SQLite volume links during server transfer
The server transfer bundle did not include the standalone_sqlite_id of
persistent volumes, so after an import an application's mount of a
SQLite data volume was no longer linked to the database. The shared
volume checks then failed: deleting the SQLite database, or the
application with its volumes, could remove the SQLite data.

The exporter now sends the SQLite database uuid with each volume, and
the importer links the volume again after all databases are imported
(with a warning when the database is not in the bundle). As a backstop,
StandaloneSqlite::deleteVolumes() skips volumes that another resource
uses, like Application already does.
2026-10-01 07:16:35 +02:00
Andras Bacsai 3c23e2d30f refactor(auth): remove OAuth re-authentication for destructive actions
Drop the OAuth confirm/callback flow that let users confirm destructive
actions by re-authenticating through a linked provider. This removes the
confirm route and session handling in OauthController, the middleware
bypass for those routes, OauthSetting::providerName(),
User::oauthConfirmationProviders(),
OauthLoginService::identityBelongsToUser() and the
socialite_reauthentication_parameters() helper.

Users with a linked OAuth identity no longer need a password step and
only use the dialog's typed confirmation. Password confirmation is now
required only for users who have a password and no SSO identity. The
confirmation modal and tests are updated to match.
2026-09-30 21:37:02 +02:00
Andras Bacsai a2cd6351b2 fix(databases): keep SSL certificate files and CA in sync with each engine
- Regenerate SSL Certificates now writes server.crt/server.key for every
  engine except MongoDB, which keeps the combined server.pem. Before, the
  button always wrote server.pem, so Postgres, MySQL, MariaDB, Redis, KeyDB
  and Dragonfly failed to start after a regenerate.
- The certificate renewal job uses the same rule, so MongoDB no longer
  switches to server.crt/server.key on renewal and fails on restart.
- Starting an SSL database writes the CA that signs its certificate to
  /data/coolify/ssl/coolify-ca.crt, so a stale CA file (for example one
  written by another server entry for the same host) no longer causes
  "certificate signature failure".
- Saving a custom CA certificate that does not match the stored CA
  private key is rejected.
- The CA file write commands live in SslHelper::caCertificateFileCommands().

Fixes #10388
Fixes #6771
Fixes #11926
2026-09-30 19:01:11 +02:00
Andras Bacsai 62d861d23a fix(jobs): resume S3 revalidation and remove orphaned notification throttles
- The S3 revalidation job stops within its time budget and continues from a
  cursor on the next run.
- An hourly sweep deletes throttle rows whose subject no longer exists.
2026-09-30 18:10:10 +02:00
Andras Bacsai d8bbd8d196 fix(storage): resolve relative file paths, keep volume options on clone, use random clone dirs
- Resolve relative LocalFileVolume paths against the resource directory.
- Create cloned volumes with the source driver and options.
- Stage clone archives for non-root users in a mktemp directory (mode 700)
  instead of a predictable path in /var/tmp.
2026-09-30 18:10:10 +02:00
Andras Bacsai 9a4469a42d fix(team): keep users of other teams when an invitation is revoked or expires
A user who accepted another team's invitation but had not set a password
yet was deleted, with their team data. Now only users in no team except
their own personal team are deleted.
2026-09-30 18:10:10 +02:00
Andras Bacsai 5921fb2958 fix(compose): keep name-only declaration for pre-existing volumes
Before Coolify kept `driver`, `driver_opts` and `labels` of renamed
Compose volumes, Docker created those volumes without them. Applying the
options now would make Docker Compose ask to recreate the volumes on
every deployment.

- Add `ignores_compose_driver_options` column to local_persistent_volumes
  and mark all volumes that exist at upgrade time
- Parsers now use composeRenamedVolumeDeclarationFor() so flagged volumes
  keep the old name-only top-level declaration
- Legacy application parsers (v1/v2) always emit name-only declarations
- Reset the flag when a volume is replicated, since a clone is a new
  Docker volume
- Include the flag in server transfer export/import; bundles without it
  default to true
- Storages UI shows which volumes ignore driver options and allows
  deleting them so the options apply on recreation
- Add tests for parsers, cloning and server transfer
2026-09-30 14:31:33 +02:00
Andras Bacsai 9e7cb60b0d feat: add import restore options, OAuth confirmation, and fixes
- database import: add keep_owners (restore PostgreSQL owners and
  privileges) and sqlite_database (pick the target SQLite file) to the
  API, OpenAPI spec, and import form
- oauth: require password confirmation and support team selection on
  OAuth login
- remote process: send team broadcast events to the team of the server
  that ran the process instead of the causer's user id
- servers API: handle the legacy build server setting
- compose: support parent bind paths and volume driver options
- dns: improve record hints and provider management
- notifications: fix throttling and missing backup notifications
- ui: rework the confirmation modal
- add feature and unit tests for the new behavior
2026-09-30 13:35:22 +02:00