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.
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.
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.
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.
- 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.
- 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
Mark DNS proposals, conflicts and server IP properties as #[Locked] so
clients cannot tamper with them. Re-resolve the zone per hostname,
lowercase hostnames, validate record content server-side and use the
resource's team id for DNS lookups and jobs.
Move modal-confirmation submitAction parsing into a tested JS helper
that strips quotes and keeps commas, parentheses and escaped quotes
inside quoted arguments. Pass the hostname to replaceManagedDnsRecord
through it instead of Js::from.
Rework member-permission tests to open the real DNS dialog rather than
setting locked properties directly.
The scheduled job manager checked every enabled schedule each minute and
evaluated its cron expression in PHP. On Coolify Cloud one run took more than
60 seconds, so runs overlapped and minutes were skipped.
Each schedule now stores its next due time:
- next_run_at on scheduled database backups, scheduled tasks, and volume
backups, and docker_cleanup_next_run_at on server settings (UTC, indexed)
- model hooks calculate it when a schedule is created or its frequency or
enabled flag changes; a server timezone change or a move to another server
calculates it again for the affected schedules
The `scheduled:dispatch` command runs the manager every minute from the
scheduler, not from a queue. It selects only due rows and claims each
occurrence with an atomic update of next_run_at, together with its delivery
record, so parallel runs on several nodes never dispatch it twice.
- database and volume backups run once when they are late; tasks and Docker
cleanups more than 10 minutes late are logged as missed
- a local time that occurs two times when the clocks go back runs once
- the migration continues each schedule after its last recorded occurrence and
drops scheduled_job_states
Show the next run, in the server timezone, on the database backup, scheduled
task, volume backup, and Docker cleanup pages, or "Invalid frequency" when the
frequency cannot be calculated. scheduled:diagnostics now lists overdue
schedules and open deliveries.
Remove workarounds that are no longer needed: the queue overlap lock and its
stale-lock repair, the catch-up window, and debug-only logs.
The floating bottom-left pill and its expanded list covered page content.
- Desktop: show a compact 'N deployments' row in the sticky sidebar footer
(spinner with count badge when the sidebar is collapsed); the list opens
to the right of the sidebar
- Mobile: show a spinner with the count in the top bar; the list opens
below the top bar
- Poll only the copy visible at the current breakpoint (wire:poll.visible)
- Show the indicator on the dashboard too, and drop the path-based
visibility logic and its navigate round trip
Adds an Images entry to the server sidebar that lists the Docker images
stored on that server: repository, tag, image ID, age, size and the
containers using each one. Images no container references are labelled
Unused and can be deleted individually; Docker's own reported total and
reclaimable size are shown above the table.
- Configuration diff: report all settings when an app has no earlier
snapshot, so older apps show "configuration changed" again (#11430).
- DigitalOcean/Vultr: do not repeat the "API error:" prefix.
- HasSecretManager: the deleting listener returns nothing, so later
deleting listeners still run.
- GitLab webhook: use new_public_id() like the other webhooks.
- Team danger zone: show the link and open the page only for the team owner.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Revert the app code changes of d666a0a95, 69eeb2f62, 1e66c9f9b and
a32082b03, so login, registration, OAuth, team danger zone, service
templates, and the other touched files are back to their earlier state.
Keep the removal of unused components and markup-only tests (d666a0a95
also contained those deletions by mistake).
StartupExecutionCleanupTest now fakes the template CDN with the current
file content, so app:init in the test no longer empties
templates/service-templates-latest.json.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Login, registration and OAuth set a fake currentTeam attribute on the user.
When refreshSession() saved the user in the same request, the query wrote
a currentTeam column that does not exist. Keep the team only in the session
and read it through currentTeam().
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Servers with caddy-docker-proxy older than 2.9 show a warning on the
Proxy page and the traffic analytics settings, with the fix.
- Caddy 2.9+ images get the basic_auth label; older ones keep the
deprecated basicauth, which Caddy 2.7 still needs.
- Caddy mounts StartSentinel::trafficLogDirectory() as /traffic, so
Caddy and Sentinel use the same access log, also in development.
- The development data volume is configurable (DEV_COOLIFY_DATA_VOLUME,
same default as before) and shared by Traefik, Caddy, and Sentinel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Traffic analytics now covers every resource on both proxies:
- Caddy: Compose applications and services get access-log labels. The
log_append key is {uuid}-{service}, the same key Sentinel reads from
Traefik router names.
- A resource owns the Sentinel key {uuid} and every key that starts
with {uuid}-. Application pages and the traffic card now merge all
keys, so Compose applications and previews show data on Traefik too.
- Global analytics: the leaderboard groups the keys of one resource
into one row, lists services, and never names keys of other teams.
- New service analytics page (project.service.analytics) with the same
view and authorization as the application page.
- Switching the proxy type restarts Sentinel when analytics is on, so
it reads the new log path. Sentinel gets no traffic mount when the
proxy has no analytics support.
- Caddy servers show a note that resources log only after a redeploy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a proxyNotRunning flag to the server sidebar state. The Proxy menu
item's warning icon now also appears when a proxy is configured but its
status is not "running". The navbar sends the flag in the
proxy-configuration-state-changed event, so the icon updates live.
Add tests for the dispatched flag and the sidebar bindings.
Wrap the danger-zone component in a bordered red-tinted card with
padding and dark-mode variants, align content to the top, switch the
title and description to neutral text colors, and add a "Permanent"
pill next to the title. Update the layout test to match.
Older Coolify versions saved Traefik configs with a router that sends
requests with the proxy container name as Host header (for example
traefik-coolify-proxy) on port 80 to api@internal. New installs no
longer get these labels, but saved configs keep them after an upgrade.
- Replace only the unchanged legacy labels with traefik.enable=false,
keep comments and formatting, and skip routers the user customized.
- A migration and GetProxyConfiguration fix the saved config in the
database only. The proxy is not restarted; the UI asks for a restart.
- Change the pending notice to "Your configuration changed, please
restart the proxy."
- Resolve conflict markers committed in the Server Proxy component,
which broke the proxy page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Proxy: list and delete Traefik ACME certificates from the server proxy
page via new TraefikAcmeService and Get/DeleteTraefikCertificate actions
- DNS: track ownership and cross-resource references for managed DNS
records so records are only deleted when no longer referenced; release
records asynchronously on resource deletion via ReleaseManagedDnsRecordsJob
and ManagedDnsRecordCleanup; harden Cloudflare provider deletion results
- Databases: fail closed on start when prerequisites or the CA certificate
are missing (DatabaseStartException, Server::ensureCaCertificate) and
clean up stale start activities via ResourceStartActivity
- Webhooks: throttle repeated manual webhook signature failures for
GitHub, GitLab, Gitea and Bitbucket
- Deployments: improve compose build-context handling and compose file
load error reporting
- Install scripts: rework terminal UI output in install.sh (stable and
nightly)
- Misc: settings sidebar accordion fixes, log drain toggle rollback,
add Serverside to README sponsors
- Add migrations and tests covering the above
Two full-width, equally weighted search boxes (global ⌘K search + settings
search) sat side by side and read as duplicates. Restyle the settings one as a
quiet inline filter: borderless recessed fill instead of a bordered box, 28px
instead of 32px, 'Filter settings' instead of 'Search settings', and a subtle
focus tint. It now reads clearly as a list filter, not a second search bar.
Build a flat search index for the application settings sidebar: every page plus
its in-page sub-sections. Each result now renders with a breadcrumb (its
category, and the parent page for a sub-section), and the query matches
sub-pages too (e.g. searching 'proxy' finds Advanced > Proxy). While searching,
the accordion is replaced by this flat result list; clearing restores it.
The application settings sidebar is long, so add a client-side search that
filters the nav items as you type. While searching it flattens the list (hides
group headers/dividers and in-page sub-sections) and shows a 'no results' hint;
clearing it restores the accordion. Extends the shared settingsSidebarAccordion
Alpine helper (search/matches/hasResults) so other sidebars can opt in later.
Follow-up to the settings-sidebar accordion. Two gaps:
- Application sidebar rendered every item's in-page sub-sections, so Advanced's
Build/Container/Deployment/… showed while you were on General. Gate the
nav-children on the active item so only the current page's sub-sections expand
(database/service/server already did this).
- The service *main* configuration page renders its nav inline (the component is
only used by service sub-pages), so it missed the accordion. Wire it up there
too.
Extends the regression test to cover both.
Allow local and S3 archives to be deleted independently when removing a
volume backup schedule, and consolidate destructive-action layouts with a
shared danger-zone component.
Discussion #11833: since v4.3.19 the resource settings sidebar expands every
group by default, so reaching Backups etc. means scrolling past every section.
Restore the v4.3.18 behaviour across all grouped resource sidebars (application,
database, service, server): each group header is now a collapsible toggle, and
only the group containing the active page is open by default. Manual expand/
collapse is remembered per resource type in localStorage; an explicit collapse
sticks even for the active group. Desktop (xl) only — the mobile grid is
unchanged (display:contents wrapper + xl-scoped collapse).
- new shared Alpine provider settingsSidebarAccordion (resources/js) + app.js
- nav-section-toggle utility (chevron header)
- wired into the four grouped sidebars
- static regression test