Commit Graph
17630 Commits
Author SHA1 Message Date
Andras Bacsai 439ee6faea fix: restrict proxy content access and scope backup storage to owners
- Hide proxy configuration contents from users without update permission.
- Select and validate backup S3 storage against the resource's team.
- Keep scheduled branch syncs green while a conflict PR remains open.
- Document external mount permissions and Sentinel traffic limits.
- Add regression coverage for authorization and backup storage selection.
2026-10-05 13:20:19 +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 2aead9c914 refactor(analytics): remove application traffic overview card 2026-10-05 11:28:03 +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 774e6bf178 feat(account): add self-service deletion and cancellation flow (#12088) 2026-10-05 09:17:10 +02:00
Andras Bacsai 34e4da1095 fix(ui): copy DNS record values on plain http instances
On non-secure origins the copy button falls back to execCommand('copy')
with a temporary textarea. The textarea was appended to <body>, outside
the x-trap focus trap of the DNS entries modal, so the trap pulled focus
back and the selection was lost before the copy. The button showed
success, but nothing was copied.

Append the textarea next to the button, select it explicitly, and
restore focus afterwards. "Copy all" now uses the shared copyButton
handler instead of its own duplicate fallback that ignored failures.

Closes #12075
2026-10-04 20:36:49 +02:00
Andras Bacsai cfe9e0eee0 fix(settings): load instance backup page without a backup schedule
/settings/backup returned 500 when the coolify-db resource existed without
a scheduled backup. Configure backup now reuses the existing coolify-db and
creates only the missing schedule for the root team, without forcing id 0.

Closes #12074
2026-10-04 20:33:22 +02:00
Andras Bacsai 16a8c7999d feat(audit): log account, resource, and instance settings changes
Add audit events for account actions, server and application settings,
services, destinations, storage, backup schedules, and scheduled tasks.
Track changed fields and skip unchanged saves without logging file content.

Add regression coverage for audit events and changed-field detection.
2026-10-04 17:32:50 +02:00
Andras Bacsai f63bf5abaa fix(sentinel): refresh UI on container health and status changes
Include health status in push deduplication and broadcast ServiceChecked after processing complete or partial snapshots.
2026-10-04 16:25:00 +02:00
Andras Bacsai e837a4f0b4 style(ui): use neutral colors for selection checkmarks 2026-10-04 13:46:21 +02:00
Andras Bacsai b235a480b2 fix(service): detect registry-prefixed Grafana server images (#10617) 2026-10-04 13:39:19 +02:00
Andras Bacsai 1b9f0ccaee feat(audit): show encrypted field-level mutation changes (#11788) 2026-10-04 13:15:46 +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 c4f96a7185 fix(analytics): label zero response status in traffic breakdowns 2026-10-04 12:42:17 +02:00
Andras Bacsai 15b64d6c56 Move proxy TLS certificates to a separate page 2026-10-04 12:35:54 +02:00
Andras Bacsai 51c1a2ad4a fix: add column headers to analytics tables 2026-10-04 12:35:53 +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 bf007b9d2d fix(docker): reduce production image size 2026-10-04 09:54:19 +02:00
Andras Bacsai e172beaa30 feat(api): add endpoints to list, get, and open preview deployments
- GET /applications/{uuid}/previews and GET /applications/{uuid}/previews/{pull_request_id}
  return preview deployments (read ability).
- POST /applications/{uuid}/previews opens a preview for a pull request and queues its
  deployment, or redeploys an existing preview (deploy ability). It accepts git_type,
  commit (required for Bitbucket), docker_tag (Docker Image apps), force, and
  instant_deploy. GitHub App and GitLab App sources set git_type automatically.
- Fix POST /deploy with pull_request_id: pass the preview git_type to the deployment, so
  deploy-key and public-repository applications build the pull request instead of the
  base branch.
- Regenerate the OpenAPI spec (also adds the missing service_name logs parameter).
2026-10-04 00:52:17 +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 22900b6d12 fix(proxy): stop automatic proxy network reconnects on status checks
Remove the periodic ConnectProxyToNetworksJob dispatch from
PushServerUpdateJob, the synchronous connect in ServerCheckJob when the
proxy is found running, and the connect-on-add in the server
Destinations component. Drop the now-unused
proxy.connect_networks_interval_seconds config option.

Replace the rate-limit/interval tests with a test asserting that no
jobs are pushed when the proxy is running.
2026-10-04 00:36:28 +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 BacsaiandClaude Opus 5.5 80b8b219a2 fix(queues): run service restarts and proxy start/restart on the deployments queue
- On cloud, RestartService and RestartServiceApplication now run on the
  deployments queue instead of high.
- StartProxy and RestartProxyJob, and the remote processes they start,
  now run on the deployments queue.
- Self-hosted still uses the high queue.
- Tests cover the new routing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 00:11:12 +02:00
Andras Bacsai b081673c98 fix(server): skip Sentinel checks on unreachable servers
- CheckAndStartSentinelJob returns early when the server is not
  functional. It now reads the container state with `docker ps --filter`,
  so an SSH failure throws and is no longer treated as a missing
  container that needs a restart.
- ServerManagerJob only dispatches the Sentinel version check for
  functional servers.
- On cloud, ServerPatchCheckJob runs on the maintenance queue and
  ServerLimitCheckJob runs on the webhooks queue.
- The default number of cloud maintenance workers goes from 10 to 60.
- excludeCertainErrors drops its unused ignore list and is typed as
  `never`.
- Docs and the Horizon config comments are updated, and tests cover the
  new behavior.
2026-10-03 23:39:38 +02:00
Andras Bacsai 083e71c481 fix(horizon): route webhook jobs to a dedicated cloud webhooks queue
On cloud, GitHub pull request webhooks and Stripe events now run on a
dedicated `webhooks` queue so a busy `high` queue cannot delay them.
Self-hosted instances keep these jobs on `high`.

- Add webhooks_queue() helper and use it in ProcessGithubPullRequestWebhook
  and StripeProcessJob
- Provision a `webhooks` Horizon pool in cloud production, sized by
  HORIZON_WEBHOOKS_PROCESSES (default 10)
- Document the new pool, rollout and rollback steps
- Cover pool provisioning and job queue routing in tests
2026-10-03 21:37:09 +02:00
Andras Bacsai 3575234b38 fix(docker-cleanup): clean after a resource stop only when the disk needs it
A stop or delete no longer queues a full cleanup each time. It skips the
cleanup when one finished on the server in the last hour, and the queued
run only cleans when disk usage is at or above the server cleanup
threshold. The docker_cleanup API flag keeps its default of true.
2026-10-03 21:25:17 +02:00
Andras Bacsai 7956be8833 fix(docker-cleanup): run one bounded cleanup per server on the maintenance queue
Scheduled, manual and stop-triggered cleanups share one per-server lock.
Stop-triggered cleanups wait 60 seconds and are coalesced per server, so
a bulk stop queues one cleanup. Remote commands share a 540 second
deadline below the 600 second job timeout, and application images are
scanned with a fixed number of remote commands.
2026-10-03 21:25:17 +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 a3ed963aed fix(server): stagger Sentinel version and patch checks per server
Servers no longer all connect at minute 0. The hourly Sentinel check uses
a per-server minute, and the weekly patch check uses a per-server time on
Sunday between 04:00 and 23:59.
2026-10-03 21:25:00 +02:00
Andras Bacsai edf1ab03a7 fix(scheduler): bound occurrence recovery and fail interrupted claims
- Recover at most 500 open occurrences per scheduler run.
- Mark claims older than the worker timeout as failed.
- Treat long-pending occurrences like lost queued jobs, and do not run
  backups late when they are older than 24 hours.
- Run fixed-time schedules once when daylight saving time repeats an hour.
- Do not run a scheduled task again when Redis hands it out after a killed
  worker, and do not overwrite a finished execution in failed().
2026-10-03 21:25:00 +02:00
Andras Bacsai c681dc0d0e fix(queue): coalesce queued container status and helper cleanup jobs per server
Queued GetContainersStatus runs are unique per server until a worker
starts them, and CleanupHelperContainersJob locks per server instead of
class-wide. Add UniqueUntilProcessingJobDecorator for laravel-actions.
2026-10-03 21:25:00 +02:00
Andras Bacsai e144b990c3 feat(horizon): add fixed-size per-queue worker pools on Coolify Cloud
Cloud production now runs one supervisor per queue (deployments, crons,
high, default, maintenance), so a busy queue cannot starve the others.
Add maintenance_queue() for slow Docker cleanups and a shared
horizon.worker_timeout config value. Self-hosted keeps the s6 supervisor.
2026-10-03 21:24:54 +02:00
Andras Bacsai c6b8dfe367 fix(status): store the status of a database or service right after it starts
After a start, the UI showed "exited" until the regular status check ran.
That check runs on the shared high queue, which can be busy (60 s on Cloud).

The start job (database) and the start task (service, on the deployment
queue) now inspect only the started containers and store their status
before they broadcast. While a health check reports "starting", they
inspect again for up to about 15 s.

ContainerStatusAggregator now builds the per-container status and the
Compose aggregation for both writers, so GetContainersStatus and the
targeted check store the same strings.
2026-10-03 21:16:44 +02:00
Andras Bacsai 599dc1ca67 fix(ssh): do not replay remote commands that failed on the server
The SSH retry matched connection words in any error output. When `mc alias set`
in a backup upload could not reach S3, its "connection refused" output caused a
retry of the full batch, and the second `docker run --name backup-of-<uuid>`
failed with a name conflict that hid the real error.

- Retry only SSH transport failures: exit code 255, or errors without an exit code.
- Keep the remote exit code on deployment command failures.
- Remove a leftover backup helper container in the same upload batch.

Closes #12059
2026-10-03 20:52:01 +02:00
Andras Bacsai f1f7272494 fix(queue): run service and database start commands on the deployment queue
On cloud, StartService and DeployServiceApplication dispatched their
CoolifyTask on the high queue, so service starts competed with all other
high jobs. remote_process() and CoolifyTask now accept a queue (default
high); start paths pass deployment_queue(). The queued RestartDatabase
action also runs on deployment_queue() instead of the default queue.
2026-10-03 20:50:49 +02:00
Andras Bacsai c0d81d4c9c fix(auth): accept Entra ID xms_edov claim as verified OIDC email
Microsoft Entra ID never sends email_verified, so existing accounts could
not be linked through OIDC. Treat a boolean true xms_edov claim (email
domain owner verified) as equivalent, via a shared
OidcUser::claimsVerifyEmail() helper used for both ID token and userinfo
claims.

Document the Entra ID issuer URL and required optional claims in the
OIDC settings helper text, and add tests covering xms_edov handling.
2026-10-02 18:51:16 +02:00
Andras Bacsai f95ddd97fa fix: enforce webhook lockouts and stop cancelled runner provisioning
- Manual webhooks (GitHub, GitLab, Gitea, Bitbucket) now reject all
  deliveries for a locked-out scope before reading secrets or verifying
  signatures, including correctly signed ones, until the window expires.
- ProvisionGithubRunnerJob checks whether the execution has already
  finished before and after each provisioning step, and only sets
  ready_at or Failed while it is still occupying/provisioning. A runner
  that is cancelled, timed out or completed mid-provisioning is no longer
  started, retried or overwritten.
- VolumeCloneJob creates bind-volume clones as plain local volumes
  without the source driver options, so the clone never mounts the
  source host directory.
- Image pulls for additional servers no longer ignore docker pull errors.
- Add tests for each change.
2026-10-02 18:24:52 +02:00
Andras Bacsai 5fd7191d4a fix(auth): allow OAuth sign-up with an unverified provider email
The provider email verification check now runs only when an account with
that email already exists. A new account follows the registration
settings, the same way password registration does, which also doesn't
verify the email. Linking to an existing account still needs a verified
email, except for password-less users created before OAuth identities.

Add tests for new-user registration from Authentik, Discord and GitLab
when the provider email is unverified. Also test that the "Registration
is disabled" error is shown when registration is turned off.
2026-10-02 17:07:54 +02:00
Andras Bacsai d6f89d1feb fix(proxy): remove legacy Traefik dashboard labels only in the traefik service
The line edits ran on the whole proxy compose file. When another service
had the same labels, the safety check failed and the dashboard fix was
skipped without a warning. Limit the edits to the traefik service block,
so other services stay unchanged and the fix is applied.
2026-10-02 17:03:55 +02:00
Andras Bacsai 8c2725a3f8 fix(scheduler): send at most one missed-run notification per hour per schedule
When queue workers are down, an every-minute task or Docker cleanup
missed a run every minute, and each missed run sent a notification.
Each missed run still gets its own failed execution, but the team now
gets at most one missed-run notification per schedule each hour,
through NotificationThrottle.
2026-10-02 17:03:55 +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 2f3a5088ee fix(ui): offset focus ring on highlighted buttons with outline gap
Highlighted buttons use the accent color, so the flush box-shadow focus
ring looked like a stray border. Use an offset accent outline instead and
keep the regular depth shadow on focus and hover-focus.
2026-10-02 16:58:42 +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 Bacsai b1ddde2b64 fix(auth): enforce Google hosted domain and block password-user linking
Treat the Google "Hosted domain" setting as an enforced Workspace
restriction: compare it to the hd claim case-insensitively with spaces
trimmed, and accept "*" for any Workspace account while still rejecting
personal Google accounts.

Exempt pre-upgrade OAuth users from the email verification check only
when they have no password. A flagged user with a password no longer gets
an unverified provider email linked to their account.

Update the hosted domain helper text and add tests for both cases.
2026-10-02 16:11:21 +02:00
Andras Bacsai 8521f973dd fix(auth): show specific error messages for denied OAuth logins
Add OauthLoginException, which carries a translation key for the message
shown on the login page. The exception message only goes to the log.
OauthLoginService now throws it for an unverified email, an account that
is already linked to another identity, and disabled registration.
OauthController shows the matching message for each case. Other
HttpExceptions fall back to a generic OAuth failure message.

The email verification check now runs before the linked-identity check,
so an unverified email cannot reveal that an account is already linked.

Add translations for the new auth.failed.oauth* keys in all locales and
cover the new error messages with feature tests.
2026-10-02 16:07:48 +02:00