450 Commits
Author SHA1 Message Date
Andras Bacsai adec9911cf Merge remote-tracking branch 'origin/main' into next 2026-11-09 10:16:31 +01:00
Andras BacsaiandClaude Opus 5.5 65dba32385 feat(server-security): scan host OS packages for vulnerabilities (#12482)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 10:12:56 +01:00
Andras Bacsai f7ea962145 fix(server): enable disabled self-hosted servers again on validation
On self-hosted, a validation that connects now clears force_disabled. The unreachable cleanup skips the localhost server, and the server page explains why a server is disabled. Cloud and transferred servers stay disabled.

Closes #12475
2026-11-06 18:57:06 +01:00
Andras BacsaiandClaude Opus 5.5 c648f4ec6a feat(ui): star servers and resources for quick access on the dashboard
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 18:16:25 +01:00
033f0c84da v4.7 (#12478)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com>
Co-authored-by: Aditya Tripathi <aditya@climactic.co>
2026-11-06 14:25:02 +01:00
Aditya TripathiandAndras Bacsai 3c33f61d7b feat(image-scan): container image vulnerability scanning with a security status per resource (#12377)
Co-authored-by: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
2026-11-06 10:31:27 +01:00
cdc45e5736 fix: merge fixes-next into next (security hardening and fixes) (#12460)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com>
2026-11-06 10:18:19 +01:00
Andras Bacsai 455b22f26c fix(deploy): explain SSH exit 255 and handle non-map compose services
- Add a hint to failed command messages when the exit code is 255,
  which explains that the SSH connection to the server closed before
  the command finished.
- Return no services from applicationParser and serviceParser when the
  compose `services` key is empty or is not a map, so parsing does not
  fail.
- Move Server::getIp() out of the sshEndpointInUse() docblock, where it
  was misplaced and broke the class.
- Add tests for the SSH hint, for services keys that are not maps, and
  for saved compose variable values that must survive a redeploy
  (#6995).
2026-11-05 09:49:35 +01:00
Andras BacsaiandClaude Opus 5.5 b83b97f7e6 fix(server): show the SSH error when a new private key cannot connect
Closes #7980

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 22:37:38 +01:00
Andras Bacsai f4a1e3e427 fix(proxy): keep Caddy WebSockets open across config reloads
caddy-docker-proxy reloads Caddy whenever any proxied container changes,
and Caddy closes every WebSocket on reload by default. Set
stream_close_delay on app, service, and dashboard reverse_proxy routes.

Closes #7942
2026-11-03 22:06:54 +01:00
Andras BacsaiandClaude Opus 5.5 ecb14e6cd5 fix(server): allow servers with the same IP and different SSH ports
Closes #7573

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 21:51:22 +01:00
Andras BacsaiandClaude Opus 5.5 5431d4954d fix(server): run the connection check Docker commands through SSH mux
Each new SSH connection opens a systemd-logind session. Some systemd
versions leak closed sessions until the host refuses logins at 8192.
The connection check now opens one new login (the probe), not three.

Closes #9148

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:40:21 +01:00
Andras BacsaiandClaude Opus 5.5 83a5667387 fix(server): detach additional-destination apps instead of deleting them on server delete
Deleting a server with "Delete all resources" (UI) or ?force=true (API)
deleted every application that used the server, also applications that
use it only as an additional destination, for example after Promote.

Server delete now deletes only resources whose main destination is on
the server. Applications that use it only as an additional destination
lose their additional_destinations row for the server, and their
containers on it are stopped (best effort). The check for resources and
the transfer archive path use the same Server::primaryResources().
The delete confirmation lists the resources to delete and the
applications that keep running on another server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 18:23:52 +01:00
Andras BacsaiandClaude Opus 5.5 ed03a31b3a fix(server): keep ip and user as strings after save
Closes #12444

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 17:52:35 +01:00
Andras BacsaiandClaude Opus 5.5 b3ec26c3b1 fix(proxy): stop routing the Reverb HTTP API through the dashboard domain
The browser uses only the /app/{key} WebSocket. Laravel sends events to
Reverb over 127.0.0.1, so the generated Traefik and Caddy configs no longer
route /apps to port 6001, and the WebSocket rule only matches /app/.

Closes #12390

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 12:01:21 +01:00
Andras BacsaiandClaude Opus 5.5 56d0cf0e27 fix(servers): validate new cloud servers automatically when they are running
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:58:34 +01:00
Andras Bacsai 101444b8ca fix(log-drains): support Axiom edge domains and update Fluent Bit to 5.1
- Add an Axiom edge domain setting (default us-east-1) and use the /v1/ingest endpoint, so EU datasets work
- Remove the Highlight drain (service ended) and stop its running containers
- Update Fluent Bit from 2.0 to 5.1 and use the warn log level
- Restart running drains when the generated files change, with the new image pulled before the old container stops
- Show log drain validation errors under their fields

Closes #9898
2026-10-27 17:41:50 +01:00
Andras BacsaiandClaude Opus 5.5 bbcadbd480 fix(backups): identify the instance database by id, not by name
A user database named coolify-db was backed up as the instance database,
which targeted the wrong container. It was also hidden from the server
database list and from status updates.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 18:14:27 +02:00
Andras BacsaiandClaude Opus 5.5 7e612422b6 fix(proxy): keep proxy status current and stop auto-start during restarts
- Refresh the navbar proxy status on the 30 s poll
- Skip Sentinel and server check auto-start while a proxy start or restart runs
- Use the StartProxy commands in RestartProxyJob, so Caddy restarts keep the
  maintenance blocks and remove the Traefik logrotate sidecar

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 18:02:17 +02:00
Andras BacsaiandClaude Opus 5.5 2ea1afb811 fix(server): explain why the Docker Engine version check failed
When "docker version --format json" did not return clean JSON (for
example, text from a shell startup file), validation stored 0.0.0 and
said that Docker was older than version 24. Validation now stores no
version and tells the user to run the command on the server. When the
version is too old, the message shows the detected version.

Closes #11089

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 00:32:14 +02:00
Andras BacsaiandClaude Opus 5.5 9db8d55473 feat(canvas): let admins drag server zones on the servers and dashboard canvases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 22:13:29 +02:00
Andras BacsaiandClaude Opus 5.5 cf9f254507 fix(server-transfer): close security holes in import, claim, and archives
Import:
- Check every uuid in a bundle against a strict id format before any
  write, and quote the SSH mux socket and key paths in local shell
  commands. A bundle uuid could run commands in the Coolify container.
- Validate bundle fqdns, git source URLs, paths, networks, cron
  expressions, and database ports; cap the number of items.
- Do not confirm that another team has a uuid.
- Refuse a project icon path with a uuid that is not a plain id.

Claim, finish, and migrate:
- Pass checklist step keys to wire:click with Js::from (stored XSS).
- Link only to https target server URLs.
- Validate target_instance_url in /transfer/complete; never prefill an
  unverified URL for a transfer back; scope the transfer-back token to
  write+deploy for 1 hour.
- Release on an owner marker only when it is newer than the own claim.
- Do not touch apps that only use the server as an additional
  destination when an archive is released or deleted.
- Give Sentinel a new token on claim.
- Add a checklist step to remove the previous instance's SSH key.
- Block a transfer while backups or scheduled tasks run.
- Authorize refreshMigration and PrivateKey checkConnection, clear
  tokens from Livewire state, hide migration log/error from members.

Archives:
- Refuse ip, user, port, private key, and Cloudflare tunnel changes.
- Throttle Enable management and require a free server slot for it and
  for a transfer back.
- Skip cloud provider calls.
- Re-read force_disabled before each SSH command, so running jobs stop.
- Probe the import check target at the resolved address only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 21:02:32 +02:00
Andras BacsaiandClaude Opus 5.5 95a3a22fdf fix(server-transfer): harden import on Cloud and stop archives from using server slots
- Rate limit the import check probe and refuse private, loopback, and
  link-local targets on Cloud.
- Rate limit the SSH validation that an import starts, like other
  server validations.
- Count only managed servers for the server limit, the limit check job,
  and the subscription page. Archives of transferred servers are left out.
- Do not show the name of another team's archived server in the import check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 20:12:59 +02:00
Andras Bacsai 6012232e97 fix(server): rate limit failed server validations on cloud 2026-10-23 11:10:02 +02:00
Andras Bacsai c454f84545 fix(server-transfer): keep imported data exact and guard archived servers
- Skip TrimStrings and ConvertEmptyStringsToNull on the import route; an empty shared variable failed the whole import
- Keep compose parser versions, static ports, legacy variable keys, and only the bundle's variables and destinations
- Give the transfer back token the deploy permission that the import needs
- Move the Cloudflare tunnel DNS record with the server and park it on the source
- Refuse cloud server deletes, power on, tunnel and DNS changes, and scp for archives; skip their backup alerts and provider polling
- Add a column-level round-trip test, archive guard tests, and a live full-coverage transfer test
- Advance the GitLab App sequence in the dev seeder
2026-10-22 15:52:20 +02:00
Andras Bacsai dd456625ec Merge remote-tracking branch 'origin/release/v4.5' into release/v4.5 2026-10-19 15:03:38 +02:00
Andras BacsaiandClaude Opus 5.5 ad6e929a93 feat(gpu): fix GPU compose settings and show detected server GPUs
- Build a valid compose device request (device_ids list, options mapping, blank driver)
- Map --gpus N to count and drop --gpus when GPU settings are enabled
- Validate only changed GPU values so stored legacy values keep working
- Detect NVIDIA GPUs and the container toolkit with the server details
- Show detected GPUs with a refresh button in the application GPU section

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 15:00:31 +02:00
22a8c09dd6 Next (#12202)
Co-authored-by: Aditya Tripathi <aditya@climactic.co>
Co-authored-by: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rosslh <ross@rosshill.ca>
Co-authored-by: Abdulfetah Suudi <suudiabdulfetah@gmail.com>
Co-authored-by: Dharmveer <51308779+dharmveer97@users.noreply.github.com>
Co-authored-by: Maxwell <136101+mxswd@users.noreply.github.com>
2026-10-19 10:07:23 +02:00
e3d4411ff2 chore: merge main into release/v4.5 (#12192)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Furox <177975472+Furox-Art@users.noreply.github.com>
Co-authored-by: johandenhollander <johan.den.hollander@finalist.nl>
Co-authored-by: João Cruz <161638790+SrJooJ@users.noreply.github.com>
Co-authored-by: Saim Hashmi <hashmisaim037@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-18 23:43:20 +02:00
Andras BacsaiandClaude Opus 5.5 7c14c0098e feat(server-transfer): run instance migration as a background job (#12180)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 22:03:45 +02:00
Andras Bacsai 448dc8ea0a feat(logging): strengthen secret redaction and add admin controls (#12151) 2026-10-13 20:43:19 +02:00
Andras Bacsai 83384a7b95 Merge remote-tracking branch 'origin/release/v4.5' into automation/sync-main-to-release-v4.5 2026-10-12 22:30:41 +02:00
Andras Bacsai 200af07225 Merge remote-tracking branch 'origin/release/v4.5' into automation/sync-main-to-release-v4.5 2026-10-12 21:38:45 +02:00
Andras Bacsai 1e42284db0 feat(proxy): override the Traefik certificate resolver per resource
Applications and service applications can now use the server resolver,
no resolver ("none"), or a custom resolver name. This allows servers
that mix Let's Encrypt domains with domains that use their own
certificates, such as Cloudflare Origin CA. The setting is available in
the UI and the API.

Closes #12138
2026-10-11 19:01:27 +02:00
Andras Bacsai af589f6da4 feat(proxy): make the Traefik certificate resolver configurable per server
Coolify always added tls.certresolver=letsencrypt to generated HTTPS
routers, so Traefik logged errors when the proxy used its own
certificates (for example Cloudflare Origin CA) without that resolver.
Add a server setting (default letsencrypt) in the proxy page and API.
An empty value omits the resolver from resource labels, the Coolify
dashboard routes, and the catch-all router.

Closes #12138
2026-10-11 10:19:40 +02:00
Andras Bacsai f39c884cc7 fix(servers): support IPv6 server addresses everywhere
Store server IPs in one form (no brackets, short lower-case IPv6) and
write each address in the form its context needs:

- URLs and host:port (Sentinel, database URLs, SFTP backup download,
  DNS servers, SMTP, webhook base URLs, links, health checks) get
  brackets through formatHostForUrl().
- Traefik Host() rules use the address without brackets, so IPv6
  domains no longer return 404; saved labels are fixed at deploy.
  Caddy keeps the brackets.
- SSH keeps the plain address.
- Hetzner gives an IPv6 /64 network; IPv6-only servers now get the
  server address (prefix::1) instead of the unusable prefix::64.
- sslip labels, registry image names with an IPv6 host, the API domain
  list, allowlist matching and duplicate checks handle IPv6.

A migration normalizes stored IPv6 server addresses, repairs Hetzner
servers saved as ::64 that are not reachable, and adds brackets to
saved Sentinel URLs.
2026-10-09 13:50:28 +02:00
Andras Bacsai 8b30b34238 feat(proxy): add custom error pages and resource maintenance mode (#12112) 2026-10-07 13:24:42 +02:00
Andras Bacsai e7025612b8 feat(hostinger): add cloud provider integration (#11176) 2026-10-07 13:14:04 +02:00
github-actions[bot] 2293cfedcf Merge remote-tracking branch 'origin/main' into next 2026-10-05 21:19:10 +00: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
github-actions[bot] edbe9020d2 Merge remote-tracking branch 'origin/main' into next 2026-10-05 10:29:31 +00: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
peaklabs-dev 174e0131f7 feat(proxy): add hashing options for HTTP basic authentication 2026-10-04 19:57:48 +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 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 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 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 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