589 Commits
Author SHA1 Message Date
Andras Bacsai 56497d50cf fix(deployments): include scheduled jobs in progress checks
Count running scheduled tasks, database backups, and volume backups,
excluding runs older than their job timeout. Report scheduled job counts
and keep the progress check active while any scheduled jobs are running.
2026-10-06 21:59:16 +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 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 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 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 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 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 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 41eb524d3e fix(dns): compare IPv6 addresses by value 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
Andras Bacsai 452259aacc feat(scheduler): dispatch scheduled jobs by their stored next run time
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.
2026-09-29 10:07:23 +02:00
Andras Bacsai af983a29d8 refactor(docker): identify containers by resource UUID instead of numeric id
Container ownership labels now use UUIDs: coolify.applicationUuid,
coolify.serviceUuid, coolify.service.subUuid and coolify.databaseUuid.
Numeric ids differ between Coolify instances, so after a server transfer
the target could not find existing containers: redeploys left the old
container running and service statuses were wrong.

Containers are matched by UUID label first, then by the compose project
(Swarm: stack namespace), which is the resource UUID since July 2024, and
last by the older numeric id label. Existing containers keep working
without a redeploy and get the UUID labels on the next deploy or restart.
2026-09-29 09:57:56 +02:00
Andras BacsaiandClaude Opus 5.5 9c7f2dc412 fix(templates): keep local service templates when the CDN bundle is unusable
A 200 response with an empty body or an error page replaced the local
templates and the shared cache, so the service list became empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:13:20 +02:00
Andras BacsaiandClaude Opus 5.5 a6a3b1d26b revert: undo the app changes from the test clean-up
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>
2026-09-28 08:06:34 +02:00
Andras BacsaiandClaude Opus 5.5 1e66c9f9bd fix(templates): keep local service templates when the CDN bundle is unusable
A 200 response with an empty body or an error page replaced the local
templates and the shared cache, so the service list became empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:18:09 +02:00
Andras BacsaiandClaude Opus 5.5 862e021f2d feat(compose): warn about legacy external volumes and list external volumes
Legacy Compose applications keep the old name of an external volume. The
deployment log now tells the user why and how to move to the external
volume. The storage page shows external volumes as a read-only list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:09:13 +02:00
Andras BacsaiandClaude Opus 5.5 49ae227f6e fix(compose): keep the old volume of existing resources for external volumes
Switching an external volume from the old <uuid>_<name> volume to the
declared external volume on redeploy could make an app look as if it
lost its data (for example a database that initializes a new, empty
data directory).

- If a resource already has the storage entry with the old generated
  name, the parsers keep using that volume and show a warning in the
  deployment log and the service start log, with the steps to switch.
- Without that entry (new resources, or external volumes added later)
  the external volume is used as declared.
- Deleting the old storage entry (storage page) switches to the
  external volume on the next deployment; the storage entry shows a
  note about it.
- Legacy Compose applications (parser versions 1 and 2) never stored
  their volumes, so they keep the old volume names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 17:24:51 +02:00
Andras BacsaiandClaude Opus 5.5 ee62f64add fix(compose): use external volumes as declared
Compose volumes declared with external: true (or external: {name: x})
were renamed to <uuid>_<name>, so containers got a new empty volume
instead of the existing one. All Compose parsers (applications,
previews, services, and the legacy parsers) now use them as written:
no uuid prefix, no preview suffix, the top-level declaration kept, and
no tracking row, so Coolify never removes a shared external volume.
External volume names must be literal; ${VAR} names are rejected.
Existing <uuid>_<name> volumes and their rows are kept on redeploy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:53:54 +02:00
Andras BacsaiandClaude Opus 5.5 700d476bac fix(dev): rewrite host paths only for the testing-host dev server
Development mode rewrote /data/coolify paths to the dev Docker volume
for every server. That is only right for the testing-host backend,
which uses the host Docker daemon. The KVM localhost and other dev VMs
have their own /data/coolify, so bind mounts, Compose content files,
the Traefik and Caddy traffic mounts, Sentinel, and database proxies
pointed to wrong paths there.

- Server::sharesDevHostDocker() and devHostDockerPath() decide per
  server; only the testing-host server (ip coolify-testing-host) gets
  the volume path. Production is unchanged.
- The testing-host volume names come from the Compose project
  (DEV_COOLIFY_DATA_VOLUME, DEV_COOLIFY_BACKUPS_VOLUME), with the
  legacy names as fallback.
- In development, saved proxy configurations with the old path are
  fixed when they load and ask for a proxy restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:21:39 +02:00
Andras BacsaiandClaude Opus 5.5 aabd127374 fix(compose): keep content files inside the resource directory
- A Compose bind volume with a Coolify `content:` block could make
  Coolify write any host file (for example /root/.ssh/authorized_keys)
  when the file was loaded. Content sources must now be inside the
  resource directory (./ paths); other sources get a clear validation
  error for services and applications (UI, API, load, deployment),
  and every content write is confined again on the server. Bind
  mounts without content are unchanged. All 94 template content
  volumes use ./ paths and still work.
- Quote container names in scheduled tasks and in the stop actions for
  applications, previews, and services.
- Fix a development-only crash when a preview with a bind mount was
  parsed (the preview suffix returned a plain string).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:37:21 +02:00
peaklabs-dev a52cafb2db Merge branch 'main' into feat/add-sqlite-database 2026-09-26 23:22:49 +02:00
🏔️ Peak edcb884266 Merge branch 'main' into feat/add-sqlite-database 2026-09-26 21:32:43 +02:00
Andras BacsaiandClaude Opus 5.5 dc20ffd92b fix(proxy): warn about old Caddy images and share the Caddy traffic log path
- 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>
2026-09-26 21:19:14 +02:00
Andras BacsaiandClaude Opus 5.5 510a2d838b feat(analytics): log Compose resources on Caddy and add service analytics
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>
2026-09-26 01:00:55 +02:00
peaklabs-dev a33fd9a36e feat(database): add sqlite as a standalone database type 2026-09-24 19:03:42 +02:00
Andras Bacsai 3f47bae0b6 fix(service): save switches without saving pending Compose edits (#11985) 2026-09-24 13:43:37 +02:00
Andras Bacsai cfe8a800cf Improve raw Compose volume path handling 2026-09-22 21:34:38 +02:00
Andras Bacsai 20a72f0f80 feat: deprecate new Docker Swarm usage and group referrers 2026-09-21 16:45:21 +02:00
Andras Bacsai ebfd4d0c1c Merge remote-tracking branch 'origin/main' into pr-11443-next
# Conflicts:
#	app/Actions/Server/StartSentinel.php
#	app/Livewire/Server/Sentinel.php
#	resources/views/livewire/project/shared/storages/all.blade.php
#	resources/views/livewire/project/shared/storages/show.blade.php
2026-09-18 12:00:04 +02:00
Andras Bacsai 1b842090ad fix(previews): split compose domains into Traefik labels (#11812) 2026-09-16 13:33:29 +02:00
github-actions[bot] 69d1e89ac2 Merge remote-tracking branch 'origin/main' into next 2026-09-16 03:14:20 +00:00
Andras Bacsai 776c56f947 fix(helper): hide deployment details unless explicitly requested 2026-09-15 11:38:33 +02:00
github-actions[bot] 02ab8db270 Merge remote-tracking branch 'origin/main' into next 2026-09-15 03:14:58 +00:00
Andras Bacsai 065ca9c861 fix(helper): prefer newer fetched helper version
Use a CDN-fetched helper version when it is newer than the bundled
version, while ignoring stale stored versions.
2026-09-14 20:50:16 +02:00
peaklabs-dev 9faa4f4b7c Merge branch 'main' into next 2026-09-09 15:32:27 +02:00
Andras Bacsai 83714ea395 fix(git): parse generic scp-style SSH URLs with custom users
Centralize scp-style Git URL parsing so user@host:path (including custom
usernames and embedded ports) is accepted and converted to HTTPS for
public clones, API create, webhooks, validation, and commit/branch links.
2026-09-08 20:33:45 +02:00
Aditya Tripathi e52390ec03 fix(team): resolve stored team on deletion and impersonation
Use resolveStoredTeam() instead of teams()->first() when picking the
next active team after a team deletion or when an admin switches into
a user's account, so a valid stored preference wins over an arbitrary
first team. DeleteTeam now returns null when the deletion leaves the
owner with multiple teams, deferring to the selection screen instead
of guessing. refreshSession also stops persisting current_team_id
while impersonating, so viewing another user's account no longer
overwrites their real last-active team.
2026-09-08 14:44:42 +02:00
Aditya Tripathi f511921895 fix(teams): guard current_team_id clear against concurrent writes
Make clearStoredTeamIfMatches perform an atomic conditional UPDATE
so a concurrent team switch isn't clobbered, and call it for the
deleting owner in DeleteTeam so their stored team id doesn't point
at a deleted team. refreshSession now falls back to
resolveStoredTeam() instead of an arbitrary first team. Add a
return type to SelectTeam::render() and tests covering owner
deletion and concurrent-selection preservation.
2026-09-08 14:44:39 +02:00
Aditya Tripathi 2b92fb86b5 fix(teams): clear stale current_team_id when membership ends
Reset the user's persisted current_team_id when they are removed from
a team, when their team is deleted, or when refreshSession finds no
team left, so a dangling reference is never restored on next login.
2026-09-08 14:44:39 +02:00
Aditya Tripathi 12498b4b86 feat(teams): persist active team and add team selection screen
Add current_team_id to users so the last active team is restored on
login instead of always defaulting to the personal team. When a user
belongs to multiple teams and has no valid stored choice, redirect
them to a new team.select screen (SelectTeam Livewire component) to
pick one, rather than silently choosing the first team. Update
Fortify and OAuth login flows to use the new resolveStoredTeam()
logic.
2026-09-08 14:44:39 +02:00
Andras Bacsai f359778115 Merge remote-tracking branch 'origin/next' into coolify-analytics-traefik-caddy
# Conflicts:
#	resources/views/components/forms/listbox.blade.php
2026-09-08 11:55:49 +02:00
Andras Bacsai 09e9822264 Merge remote-tracking branch 'origin/next' into automation/sync-main-to-next
# Conflicts:
#	app/Actions/Database/StartDatabase.php
#	app/Models/StandaloneClickhouse.php
#	app/Models/StandaloneDragonfly.php
#	app/Models/StandaloneKeydb.php
#	app/Models/StandaloneMariadb.php
#	app/Models/StandaloneMongodb.php
#	app/Models/StandaloneMysql.php
#	app/Models/StandalonePostgresql.php
#	app/Models/StandaloneRedis.php
#	resources/views/livewire/project/shared/storages/all.blade.php
#	tests/Feature/PersistentStorageVolumesLayoutTest.php
#	tests/Feature/UserSeederTest.php
2026-09-08 10:02:12 +02:00
Andras Bacsai c68259cddf Merge remote-tracking branch 'origin/next' into coolify-analytics-traefik-caddy 2026-09-07 20:28:42 +02:00
Andras Bacsai 183e28682c fix(domains): route Compose domains using service ports
Resolve routing ports per Compose service, preserve legacy embedded ports, and ignore non-TCP declarations.
2026-09-07 15:17:23 +02:00
Andras Bacsai 208f7720bc fix: avoid inherited compose ports and defer archive inspection
Prevent multi-service Compose domains from inheriting the application port, and defer PostgreSQL custom-format archive inspection to pg_restore.
2026-09-05 14:19:38 +02:00
Andras Bacsai 3c0e43f482 feat(settings): configure CDN URL for stored images
Add a persisted instance setting for S3 image CDN URLs and use it when building image links. Cache profile avatars and project icons with immutable one-year headers.
2026-09-04 21:15:06 +02:00
Andras Bacsai 986ece457d fix(domains): use Compose service ports for internal routing
Detect Docker Compose service ports for domain internal ports and proxy labels, with application ports as a fallback.
2026-09-03 20:38:19 +02:00