- Backup pages show Backup, and service sub-resources show their own
name, because the row opens that menu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- On a resource page, the main menu starts with a row that shows the
resource name and opens the deepest resource menu again.
- Pass the resource name to registerMainNavContext() again.
- Document the row in DESIGN.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Save build_server_id as soon as a build server is selected, so the cancel
cleanup finds the helper container on the build server. Let the final
helper stop run after a cancel.
Closes#12383
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
An invited user who set a password with Forgot password kept the forced
reset flag. A re-invite then replaced the password, and a revoked or
expired invitation deleted the account.
Re-inviting an email after its invitation expired, or re-inviting a user
who never set a password, sent a link that needs a login the user cannot
complete. Unused invited accounts now get a magic link again, and changing
another user's password no longer ends the current session.
Two applications that build the same Docker image name push the same
tags (commit SHA, latest, pr-<id>), so one can run the image of the
other. Deployments, the General form, and the API now reject such a
name for applications of the same team.
Closes#9834
Key the loaded view so the lazy-load morph replaces the placeholder
skeleton instead of patching it in place. Alpine did not start the
Locations toggle, the globe, and the table pagers on reused nodes.
Closes#12370
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Click a type, status, server, source, or role value in a table row to
filter the table by that value. Click it again to remove the filter.
Covers the environment resources, deployments, application and service
backups, and team members tables.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ServerManagerJob and the Docker cleanup step of ScheduledJobManager
load the settings of every server with SELECT *. That reads the
patch_check_result JSON, which holds the full update list of each
server. With 12,709 rows, the scan took about 4 ms, but the full fetch
took 390-536 ms and 294 MB of PHP memory. Without the column it took
74 ms and 111 MB.
Add ServerSetting::withoutPatchCheckResult() and use it in both loops.
Queued jobs reload the full settings through SerializesModels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An OR of two correlated EXISTS subqueries made Postgres estimate a
per-server scan of the runner executions, so JIT compilation took most
of the time of the per-minute reconcile query. Select the server ids
from both runner tables with one UNION and index executions by status
and server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When another process held the control socket, `ssh -fN` disabled
multiplexing and stayed open as a plain connection forever. Each one
kept an sshd process on the server, which used up its memory.
Run `true` instead of -fN. ControlPersist moves a new master to the
background; a process that cannot become the master exits.
Public pull request previews no longer get production values, and no
preview falls back to production variables. Existing previews were
deployed with these values; for example, their databases were
initialized with the production password, so they broke on redeploy.
Previews that exist at the upgrade get keeps_production_values and
keep the earlier behavior. Previews created later do not. Server
transfer moves the flag.
The retry labels (Traefik retry middleware, Caddy lb_try_duration)
changed the labels of every application. On the first rolling update
after a settings save, the old and the new container defined the same
Traefik router differently, so Traefik dropped the route and returned
503 until the old container stopped.
Remove the retry labels from this release. The rolling update drain
wait stays.
Webhook secrets are stored settings, so a token with the write ability can
set or change them, the same as GitHub App and GitLab App webhook secrets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 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
Cloning a clone added another "-clone-<id>" to the name, so after about
seven clones the name was longer than 255 characters and the insert failed.
Closes#9976
The log endpoints found containers with `docker ps -a`, then rejected
every container that was not running. Remove that check, so failed
deployments can be diagnosed through the API. Without service_name,
the application endpoint prefers a running container. The old error
stays when no container exists.
Closes#9972
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The analytics page fetched each server's dashboard bundle over SSH one
after another, so the skeleton stayed until every server answered. Start
all fetches at the same time and remember failures, so the page waits
only for the slowest server.
Closes#12347
Provider errors now return 502 with a safe message, the provider status, and
the provider error code instead of a bare 500. Hetzner no longer sends a create
request again after a 5xx, and a VPS that Coolify did not save is deleted.
Proxies started before v4.4.4 kept the old nginx config after an upgrade:
the upstream IP was resolved only once and IPv6 connections were reset.
- Label new proxies with a configuration version
- Status checks (Sentinel and SSH) recreate a running proxy with an
older or missing version once, at most every 10 minutes
- Remove the old proxy only after the image pull, so a failed pull
keeps it running
Closes#10198
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a "Use path-style URLs" setting to S3 storages. It is detected from
the endpoint (Alibaba OSS, Tencent COS, Volcengine/BytePlus TOS), and the
connection test switches to virtual-hosted style when the provider
rejects path-style requests. Backups pass `--path off` to mc.
Closes#10347
The deploy key flow saved the base directory only for Docker Compose.
All repository flows now validate the base directory, and the GitLab
and Gitea flows reject destinations on servers that cannot deploy.
Closes#10319
Install, build, and start commands saved before they were validated
blocked every save of the General tab, and the fields are hidden for
build packs other than Nixpacks and Railpack, so users could not fix
them. Validate these commands only when they change; deployments pass
them to the builder as one escaped argument.
Closes#10055
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Laravel app on a sibling subdomain with SESSION_DOMAIN=.example.com sets
a second XSRF-TOKEN cookie that the browser also sends to Coolify. The
terminal server forwarded only the first one, so Laravel answered 419 and
the terminal closed with 4401. Try each distinct token (at most five) and
keep the accepted one for the requests that follow on the connection.
Closes#10397
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>