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.
- Manual webhooks (GitHub, GitLab, Gitea, Bitbucket) now reject all
deliveries for a locked-out scope before reading secrets or verifying
signatures, including correctly signed ones, until the window expires.
- ProvisionGithubRunnerJob checks whether the execution has already
finished before and after each provisioning step, and only sets
ready_at or Failed while it is still occupying/provisioning. A runner
that is cancelled, timed out or completed mid-provisioning is no longer
started, retried or overwritten.
- VolumeCloneJob creates bind-volume clones as plain local volumes
without the source driver options, so the clone never mounts the
source host directory.
- Image pulls for additional servers no longer ignore docker pull errors.
- Add tests for each change.
The failed-authentication lockout for manual webhooks was checked before
the signature, and the bucket used the client IP from X-Forwarded-For.
An unauthenticated client could send failed attempts with the Git host's
address for a public repository and branch, and correctly signed
deliveries then got 429, so deployments were lost.
The lockout now applies only to deliveries that fail authentication; a
delivery with a valid secret for a matched application is always
processed. Failures are counted per connection address (REMOTE_ADDR, or
CF-Connecting-IP on cloud), so rotating X-Forwarded-For no longer gives
a new bucket.
- 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>
- TrustHosts: validate the Host header also when the FQDN cache is cold.
- Settings email: authorize the SMTP and Resend toggles.
- Server navbar: show Sentinel status only to users who can view Sentinel.
- GitHub source: restore permission gates on Sync name and Delete, and the
Test connection button.
- Profile: make avatar upload work (missing import and file uploads trait).
- API tokens: keep the "Never" expiry choice.
- Environment variables: show validation errors instead of a generic error.
- Servers API: read the server uuid from the route in domains_by_server.
- Configuration diff: report all settings when no earlier snapshot exists.
- Cloud servers: restore Power On for stopped servers; do not repeat the
provider error prefix.
- HasSecretManager: do not stop later deleting listeners.
- GitLab webhook: use new_public_id() like the other webhooks.
- Dev: show the Lima VM start command on the localhost server page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every webhook handler (GitHub, GitLab, Gitea, Bitbucket, GitHub App,
GitLab App) now reads the fields it uses through typed readers: refs,
titles, and actions must be strings, ids positive integers, commit
SHAs 7-64 hex characters, and URLs http(s). An invalid value returns
"Nothing to do. Invalid '<field>' in the request." without a
deployment. Signature checks and the failure lockout are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- GitLab, GitHub, and Gitea crashed with a 500 on push payloads without
a commit list, and other malformed payloads (missing repository,
project, ref, Bitbucket changes, non-string values, unsupported
Gitea events) also returned 500. They now get a clean response; a
push with an unknown file list still deploys, and a branch deletion
does not deploy.
- The manual webhook lockout counts only distinct failed attempts: the
same wrong GitLab token, or an identical HMAC redelivery, counts
once, so a misconfigured hook no longer locks out a valid one. Every
new guess still counts (30 per scope and minute). Attempts are
stored only as HMAC hashes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Failed manual webhook deliveries were counted per provider and source
IP. On Coolify Cloud many customers share the Git provider IP, so one
repository with a wrong secret locked out valid deliveries of all
other repositories for 60 seconds.
The failure key now also contains the repository and branch. Guessing
the secret of one application stays limited to 30 tries per minute,
and a lockout rejects all deliveries in that scope, also correct ones.
A GitLab request without a token no longer counts as a failure.
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
Webhook secret was stored and shown as plaintext. Use a password field,
encrypt at rest (with legacy plaintext read support), and look up tokens
via findByWebhookToken so encrypted values still authenticate webhooks.
GitlabAppPolicy previously allowed any authenticated user to update,
delete, and create GitLab sources. Align it with GithubAppPolicy, require
Application create authorization on the private-repo wizard, and reject
OAuth callbacks from non-admins so members cannot escalate privileges.
Adds self-hosted GitLab OAuth sources so Coolify can connect to a self-managed GitLab instance, list private repositories, clone over an OAuth token, and deploy (the GitLab counterpart to GitHub Apps).
Hardening: authenticated, one-time team-bound OAuth callback state; token redaction in deploy logs; custom host port/path kept in clone and ls-remote URLs; submodule OAuth auth; system-wide source selection. Covered by unit and feature tests.
cosigned by OpenAI Codex at M1 Max
The manual webhook handlers selected target applications with a
`git_repository LIKE %full_name%` substring query, so a payload
repository name could match unintended applications when repository
names overlap.
Add a `MatchesManualWebhookApplications` trait that validates the
incoming `owner/repo` value and matches `Application.git_repository`
by exact normalized path. Github, Gitlab, Gitea and Bitbucket manual
handlers now use it, reject invalid repository input early, and return
a consistent generic webhook failure payload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add DetectsSkipDeployCommits trait with two strategies: shouldSkipDeploy
(all commits must contain the marker) for push events, and
shouldSkipDeployAny (any single marker triggers skip) for PR/MR titles
and latest-commit signals.
Apply trait to Bitbucket, Gitea, GitHub, GitLab webhook controllers and
ProcessGithubPullRequestWebhook job. PRs pass pullRequestTitle through
to the job constructor for evaluation.
Introduce a dedicated `audit` log channel (daily rotation, configurable retention via
LOG_AUDIT_DAYS) and a small `auditLog()` / `auditLogWebhookFailure()` helper used to
record state-changing API operations and webhook events.
Instrumented:
- API mutation endpoints (create / update / delete / start / stop / restart) across
applications, services, databases (incl. backups, env vars, storage), servers,
projects + environments, scheduled tasks, private keys, GitHub apps, cloud provider
tokens, Hetzner server provisioning, instance enable/disable.
- Webhook signature verification outcomes for GitHub, GitLab, Bitbucket, Gitea and
Stripe, plus the Sentinel push endpoint.
- Authentication and authorization outcomes via the global exception handler and
the `ApiAbility` middleware (unauthenticated, ability-denied, policy-denied).
The helper is wrapped in try/catch so logging failures never affect the request
path. Successful operations log at `info`; suspicious/denied requests log at
`warning`. Operators wanting a failures-only feed can set `LOG_AUDIT_LEVEL=warning`.
Includes a feature test suite covering the helper, the webhook providers and the
new auth/authorization log paths.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Auto-generate a 40-char random secret for each manual_webhook_secret_* column on Application creation so new apps are never left with an empty secret.
- Add encrypted cast for the four webhook-secret columns; backfill migration re-encrypts existing plaintext values and fills missing ones.
- Reject webhook deliveries when the stored secret is empty (GitHub, GitLab, Bitbucket, Gitea manual endpoints).
- Bitbucket: require the sha256 algorithm prefix on X-Hub-Signature instead of trusting the client-supplied algo.
- GitLab: drop the ?? '' fallback on the token comparison.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace all uses of `forceFill`, `forceCreate`, and `forceFill` with their
non-force equivalents across models, actions, controllers, and Livewire
components. Add explicit `$fillable` arrays to all affected Eloquent models
to enforce mass assignment protection.
Add ModelFillableCreationTest and ModelFillableRegressionTest to verify that
model creation respects fillable constraints and prevent regressions.
Replace create() with forceCreate() across internal model creation operations to bypass mass assignment protection. This is appropriate for internal code that constructs complete model state without user input.
Add InternalModelCreationMassAssignmentTest to ensure internal model creation behavior is properly tested. Optimize imports by using shortened Livewire attribute references and removing unused imports.
Replace insecure !== operator with hash_equals() for constant-time
string comparison when validating GitLab webhook tokens.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds Retry-After: 60 header to all deployment queue full responses,
helping webhook clients know when to retry their requests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Create a shared CleanupPreviewDeployment action that unifies PR cleanup logic across all Git providers. Previously, GitHub had comprehensive cleanup (cancels active deployments, kills helper containers, removes all PR containers), while GitLab, Bitbucket, and Gitea only did basic cleanup (delete preview record and remove one container by name).
This fix ensures all providers properly clean up orphaned PR containers when a PR is closed/merged, preventing security issues and resource waste. Also fixes early return bug in GitLab webhook handler.
Fixes#2610🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add configurable deployment_queue_limit server setting (default: 25)
- Check queue size before accepting new deployments
- Return 429 status for webhooks/API when queue is full (allows retry)
- Show error toast in UI when queue limit reached
- Add UI control in Server Advanced settings
Fixes#6708🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This feature stored incoming webhooks during maintenance mode and replayed them
when maintenance ended. The behavior adds unnecessary complexity without clear
value. Standard approach is to let webhooks fail during maintenance and let
senders retry.
Removes:
- Listener classes that handled maintenance mode events and webhook replay
- Maintenance mode checks from all webhook controllers (Github, Gitea, Gitlab, Bitbucket, Stripe)
- webhooks-during-maintenance filesystem disk configuration
- Feature mention from CHANGELOG
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>