Replace datalist and searchable-listbox components with the shared listbox and migrate form fields.
Deduplicate superseded Livewire updates to prevent repeated array update hooks, with regression coverage.
Add an authorized update button with activity logs to pull the latest image and recreate the tunnel container in the background.
Persist new Compose files under /data/coolify/cloudflared and support existing /tmp configurations. Cover updates, missing configuration, and member restrictions with tests.
Combine Sentinel overflow rows into one unlinked "Other paths" row.
Show domains beside paths and include them in Livewire row keys.
Add regression coverage for overflow aggregation across app keys.
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.
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.
A staged upload always won over a server path, so an old upload that
was never imported could be restored instead of the chosen file.
- Record the selected source (checked server file or finished upload)
and import only from it
- Delete a stale staged upload when another source is used
Blade does not compile @js() inside component tag attributes, so Alpine
got the literal text and threw an expression error.
- The environment variable editor closes again after an update
- The change email button in the profile gets a valid disabled state
Login no longer accepts invitations, so the dashboard lists the
unexpired invitations for the user's email with a link to the
invitation page, where the user accepts them.
Check start prerequisites in database and deploy APIs and MCP controls.
Return HTTP 422 for blocked API starts and restarts. Show restart notices
and open UI logs only after an operation is queued.
Add API regression tests for blocked ClickHouse starts and restarts.
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.
- 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.
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.
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
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.
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.
- 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.
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.
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.
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.
- Only password-less users from before OAuth identities skip the provider
email verification claim. Otherwise an unverified provider account with
the email of a password user (including root) could sign in as them.
- Build the default callback URL from the instance URL, not the request
Host header, and do not save it during a login.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
New or changed manual webhook secrets (GitHub, GitLab, Bitbucket,
Gitea) must have at least 16 characters, in the Webhooks page and in
the application API. Stored secrets keep working: an unchanged value
passes, and an empty value still disables the webhook. Each secret
field gets a Generate button that fills a random 40-character secret;
the user saves it after adding it to the Git provider.
- refreshEnvs is now sent only to the environment variable list, and
rows no longer listen to it, so adding or removing variables cannot
queue an update for a removed row ("Snapshot missing"). Locking a row
refreshes that row directly; the compose editor gets its own event.
- saveCustomName() no longer saves a custom container name that another
application on the server already uses; it restores the stored value.
- Cancelling a GitHub runner uses the Coolify confirmation modal instead
of a native browser dialog.
Add optional per-option description support to the listbox component,
rendered as muted text below the option label.
Use it on the server role selector (new server and server settings) to
explain each role, and note that GitHub Actions runners require the
"Builds only" role in helper texts and the runners empty state.
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.
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.
Reword the legacy-volume notice to say the resource must be stopped and
data backed up before deleting the entry and Docker volume.
In the delete modal, explain what happens with and without deleting
the Docker volume. Add a warning that the resource must be stopped
first, because Docker cannot remove a volume that a running container
uses, and suggest a backup before deleting.
Domain Connect used the unfiltered server IPs, so a private address could be
published. It now uses the first public IP and refuses with the no public IP
message when there is none, like automatic DNS.
- The certificate delete dialog no longer lists a proxy restart it does not
perform, and its warning mentions the backup.
- Confirmation dialogs list actions with a dash instead of a trash icon.
- Error buttons get a red focus ring instead of the accent ring.
- Use existing icons for the TLS empty state and built-in shared variables.
An all-databases MySQL/MariaDB restore also restored the mysql system
database, so after the next restart the root and app passwords were the
source ones and the MariaDB health check failed. The restore now skips mysql,
sys, performance_schema and information_schema by default. The new option
"Restore users and privileges" (restore_mysql_users in the API) keeps the
old behaviour.
Coolify copies acme.json to a timestamped backup before every change and
keeps the last 10. The proxy page lists the backups with Restore and Delete
for team admins, and the restore dialog can restart the proxy right away.
Changing the Vault base URL or namespace, or the Infisical base URL or
client ID, sent the stored secret to the new settings. The secret must now
be entered again, and base URL changes are audit logged.
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
- 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