- Remove tests that only check markup, CSS, static text, wire:key strings,
framework behavior, copied app logic, or expect(true)
- Replace source-grep and Reflection checks of security fixes with tests
that run the real code (shell quoting, locked properties, 403 checks,
webhook fork trust, log redaction, Sentinel token cleanup)
- Assert response bodies and unchanged data on API error paths
- Run the Traefik version job through handle() instead of private methods
- Update stale tests for scheduled task props, the default Postgres image,
and the disabled transfer link of unvalidated servers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Require the write permission and update policy to open a preview or
change its pull request URL, git type, or docker tag via the API;
deploy-only tokens can only redeploy an existing preview
- Reject rollbacks to commits that are neither a listed rollback image
nor a successful production deployment (GetRollbackImages::allows)
- Validate git_repository with ValidGitRepositoryUrl when it changes on
application update
- Add RejectPublishedAppKey global middleware
- Show a "still starting" message when the root setup window is pending
- Document the new 403 responses and constraints in the OpenAPI spec
- Add tests for the new checks
- Use the limits of the API token in more permission checks.
- Show a clearer message when a token limit blocks a change.
- Describe API tokens with limits in SECURITY.md.
Scanning is now controlled per server and per app. Each server downloads
the Grype database from its default source.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Restores, downloads, and the import check refuse an SFTP storage without a saved host key.
- Only Validate, Save, Create, and the API validate endpoint save a new key; backup and
revalidation jobs do not.
- An endpoint change or a host key reset marks the storage unusable.
- Save the key only when the endpoint did not change during the connection test.
- Restore the saved SMTP/Resend flags when a provider switch fails validation, so a
later event toggle cannot save a half-configured Resend (team and instance pages).
- Resend uses smtp_from_* when resend_from_* is blank, as before the Resend sender fields.
- Require the Resend sender only when Resend is enabled.
- Replace wire:confirm in the SMTP server picker with x-modal-confirmation.
GitHub rejects new RSA SSH keys smaller than 3072 bits, and phpseclib
made 2048-bit keys by default, so new RSA deploy keys from Coolify
could not authenticate.
Closes#12461
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Read compound and fractional durations (1m30s, 1.5m), quoted values,
only the flags before CMD, the escape parser directive, and NONE with
flags. Zero and sub-second values no longer become 0 seconds.
Closes#12455
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Containers without the Coolify default variables sent no project,
environment, or service to the drain. The fluentd driver now also sends
the Coolify container labels; the built-in configs use them as a
fallback and add coolify.service_name.
Closes#8415
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Containers without sh (distroless or scratch images) could not run
scheduled tasks, because Coolify always ran the command with sh -c.
Turn off "Run in a shell" to split the command into arguments and run
it directly. The option is also in the API, the MCP tools, and the
server transfer.
Closes#8244
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Docker Compose gives a recreated container the old mount at each path
that the image declares with VOLUME, also a volume deleted in Coolify.
Before `up`, remove only containers that still mount a Coolify volume
whose storage entry was deleted. The stale volume delete dialog now
warns when a container still uses the volume.
Closes#6875
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MinIO client rejects endpoints such as `/storage/v1/s3`. Transfers to
these endpoints now use curl with a presigned URL that Coolify signs, for
database backups, volume backups, and S3 restores.
Closes#8530
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A preview deployment set COOLIFY_BRANCH in three places with three
values: the Compose shell variables used the application branch
(main), the Compose parser used pull/<id>/head, and other build packs
used the fetched refspec (pull/<id>/head:pr-<id>-coolify) or the
application branch. A variable that refers to ${COOLIFY_BRANCH} then
got a different value than the container. All paths now use
Application::coolifyBranch(), which gives pull/<id>/head for previews
and git_branch for production deployments.
Closes#9572
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Rybbit template had a proxy route only for the client, so the
/api requests of the browser got a 404 from the client and sign-up
and login failed. The backend now declares SERVICE_URL_RYBBIT_3001=/api
on the same host and keeps the /api prefix. The backend is listed
before the client, so SERVICE_URL_RYBBIT (used in BASE_URL) stays the
root URL.
Closes#9055
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stop calling /zen before each installation token request. A short
network problem to GitHub failed deployments with a cURL timeout on
/zen. assertGithubClockInSync now takes the failed response and reads
its Date header, so the "System time is out of sync" error still shows
when GitHub rejects the JWT.
Call it on failed JWT requests in the webhook installation check, the
permission job, the source connection test, and the server transfer
checklist. Remove the /zen fakes from the tests.
S3 storages with a self-signed or private CA certificate failed with
"cURL error 60", because the Coolify container and the backup helper do
not use the CA certificates of the host. The new opt-in setting turns off
the certificate check in the PHP S3 clients and sets MC_INSECURE for mc.
Closes#9762
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The MCP deploy tool and control start skipped the deployment queue, so
two rolling updates of one application could run at once and remove the
new container of each other. A rolling update now also removes only the
containers that ran before it started.
Closes#12442
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Traefik renews every certificate in acme.json, also when no route uses
its domain. The TLS certificates page now marks certificates that no
Traefik label, proxy config file, or resource domain on the server uses,
and a "Delete unused" button removes them in one write with one backup.
Closes#12447
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use the only reported disk when no known root mount is reported, and stop
filtering disk history by mount names so Sentinel 1.1.4+ keeps that disk.
Closes#12448
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mc keeps one --resolve IP per host, and Coolify always pinned the IPv4
address of a dual-stack endpoint. On servers without IPv4, uploads failed
with "network is unreachable". A helper container on the upload network
now connects to each validated IP, IPv4 first, and the first IP that
connects is pinned. S3 restores are now pinned in the same way.
Closes#12397
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Once Coolify is healthy, install.sh switches the instance to that update channel with php artisan app:update-channel
- The command and the update page share the rule that keeps the updates of the old channel after a switch to a more stable one
- Every channel has a block of its own, every version after this one reads only these blocks
- Every stable sync also fills the coolify and traefik keys older instances read
- An instance keeps a copy of versions.json and reads upstream.json of the image until it has one
- Removes coolify-testing-host.yml too, it published the testing host to Docker Hub for docker-compose.windows.yml alone, the development setup builds it locally
- Adds the channels stable, preview, nightly and custom
- Preview and nightly read /coolify/release.json on the CDN and take the newest version of their channel and the more stable ones, nightly gets a new build of next or the release branch twice a day
- Custom installs any image tag of ghcr.io by hand, such as pr-1234 for the build of pull request 1234
- After a switch to a more stable channel, the instance keeps the updates of the channel it left until the stable release of its version is out
- The local versions.json moves to storage/app and is replaced in one step, a channel change replaces it without the version of the old channel
- Docker Compose builds the development helper from the main branch of that repository, scripts/dev-helper from there or from a local checkout
- Remove buildHelperImage from the settings, nothing calls it since its button was removed
- The upgrade script removes COOLIFY_VERSION from .env, so a tag such as nightly or pr-1234 is never taken for the version
- install.sh still takes a version such as 4.6.0-beta.1, LATEST_IMAGE in .env picks the image and the image carries its version
- Remove other/nightly, the nightly channel uses the production files, so there are no longer two copies to keep in sync
- sync-cdn.sh --nightly syncs the files of the commit the nightly is built from to /coolify/nightly on the CDN, with the nightly version in its versions.json and in /coolify/release.json, and pushes them straight to main of coollabs-cdn
- version.sh picks the branch the nightly comes from, the release branch of the next version after the newest stable one or next, and names the version <preview>.<YYYYMMDD>.<N>
- The image is labeled with its version and commit, the nightly workflow reads both from the image nightly points at to count up N and to skip a run without a new commit
- config/constants.php reads the Coolify and helper versions from them
- The image and sync-cdn.sh write versions.json from upstream.json, install.sh reads it by key
phpdotenv trims whitespace around an unquoted value, including the \r of
CRLF line endings. The repair quoted such values, which broke valid lines in
CRLF files and kept spaces that phpdotenv drops. Trim the value first and
quote it only when a space is still inside it.
Closes#12375
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Installers before 4.5.1 and manual edits could leave ROOT_USERNAME (or the
root email or password) with spaces and no quotes. The running container
ignores .env because its config is cached, so the next upgrade recreated it
and phpdotenv failed, which made every page return HTTP 500. The upgrade now
quotes these values before it restarts Coolify.
Closes#12375
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>