Commit Graph
1825 Commits
Author SHA1 Message Date
Andras Bacsai 62be8674bc feat(proxy): add optional permanent redirects for apps and services (#12233) 2026-10-19 21:46:12 +02:00
Andras BacsaiandClaude Opus 5.5 63153aa8b4 fix(cloud): rework unsubscribed team cleanup and account deletion (#12210)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 17:32:16 +02:00
Andras Bacsai 975048bad8 fix(security): encrypt plaintext secrets at rest 2026-10-19 12:28:23 +02:00
Andras Bacsai 7fe40f4cf6 fix(api): require deploy ability for server side effects
Require deploy permission for API changes that trigger server side effects, including resource deletion, storage access, provider operations, and database imports. Also escape Docker cleanup filters and sanitize log drain server names.
2026-10-19 09:07:42 +02:00
Andras BacsaiandClaude Opus 5.5 0eeb630ccd fix(compose): use explicit compose file paths and remove volumes by name (#12199)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 00:00:48 +02:00
Andras Bacsai 5227693885 fix(deployments): scope deployment queries to team applications (#12195) 2026-10-18 23:32:14 +02:00
Andras BacsaiandClaude Opus 5.5 8ed31bb96e feat(env): restore Coolify default variables with a per-resource option
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 12:55:53 +02:00
Andras Bacsai 34b3a18307 fix(compose): resolve ../ bind sources in raw compose mode
oldRawParser made the bind directory of a long-syntax source like
../data at {workdir}./data, next to the path that Docker Compose uses.
Resolve the source with replaceLocalSource(), as the compose parsers do,
so Coolify makes the directory that the container mounts. A source that
resolves above / now stops the deployment before remote work.

Closes #4055
2026-10-18 12:09:42 +02:00
Andras Bacsai 0e3b6e5aa6 fix(databases): apply custom configs on servers with a strict umask
A custom config file written with tee gets the server umask. With umask
027 or 077 the file is not readable by the container user, so MySQL and
MariaDB ignored the whole config without an error.

- Set mode 644 on the MySQL, MariaDB, PostgreSQL, and MongoDB custom
  config files after each write.
- Close the multiplexed SSH connection when the server user, IP, or port
  changes, so the next command does not run as the old user.

Closes #11600
2026-10-16 21:28:21 +02:00
Andras Bacsai 12fd9e448b fix(upgrade): retry rate limited image pulls and spread auto updates
Closes #11890
2026-10-15 23:36:47 +02:00
Andras BacsaiandClaude Opus 5.5 3414b88e0d fix(applications): ignore missing health on additional server status
Closes #11892

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 23:33:52 +02:00
Andras Bacsai d60d3a5b9d fix(service): replace removed MinIO images with pgsty/silo
Closes #11975
2026-10-15 23:32:06 +02:00
johandenhollanderandClaude Opus 5.5 c79f48b6b8 fix(previews): delete compose preview storage records on cleanup (#12006)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 23:13:44 +02:00
Andras BacsaiandFurox 6cf1516749 fix(server): accept OS derivatives through ID_LIKE in validateOS
validateOS matched only ID from /etc/os-release, so derivatives such as
Omarchy, CachyOS, Manjaro or Linux Mint failed validation before the
Docker checks ran. Check ID first, then each ID_LIKE entry, and keep the
exactly-one-match rule. Move the parsing into a testable function.

Closes #12033

Co-authored-by: Furox <177975472+Furox-Art@users.noreply.github.com>
2026-10-15 22:35:45 +02:00
Andras BacsaiandClaude Opus 5.5 7466da12a2 fix(git): accept ssh:// repository URLs with custom ports
The repository URL rule rejected ssh://user@host:port/path, so deploy-key
resources on servers with a custom SSH port needed a manual URL edit after
creation. Accept ssh:// URLs and keep them unchanged for deploy keys so the
port reaches the clone. Rename scpStyleGitUrlToHttps to sshGitUrlToHttps and
let it convert ssh:// URLs too, so public flows, repository links and GitHub
App repository slugs handle them.

Closes #12161

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 22:30:26 +02:00
Andras Bacsai 2c973d68c4 fix(github): paginate PR files for watch paths and reuse installation tokens
- Fetch every page of pull request files so watch paths match PRs with more than 30 files.
- Deploy the preview and log a warning when the changed-file list is unknown (API error or truncated compare list).
- Log when watch paths decline a preview deployment.
- Keep the GitHub App installation token in memory per instance, so a deployment requests one token instead of 3-5.

Closes #12169
2026-10-15 14:45:45 +02:00
Andras Bacsai e071b11b7b fix(sentinel): push local Sentinel to coolify:8080 for any host gateway URL
Closes #12171
2026-10-15 14:20:38 +02:00
Andras Bacsai d196dd322f fix(deploy): keep start-time buildpack vars in runtime env (#12025) 2026-10-14 09:43:55 +02:00
Andras Bacsai 7837a07ba2 fix(notifications): disable channels after repeated configuration failures
Count invalid keys, tokens, and deleted webhooks in a new failure_count column
on each notification settings table, like servers.unreachable_count. After 3
failures in a row the channel is disabled and the team is told by instance
email and its other channels. Rate limits and server errors are retried and
not counted; test notifications never count.
2026-10-12 14:37:58 +02:00
Andras BacsaiandClaude Opus 5.5 213a61b7ab fix(env): pass exact env values to containers, keep old escaping for existing variables (#12024)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:06:45 +02:00
Andras Bacsai cdd8f82840 fix(compose): preserve environment variable semantics (#11794) 2026-10-11 21:02:11 +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 BacsaiandClaude Opus 5.5 d4502bc514 fix(previews): avoid doubled scheme in preview URL templates
Closes #12136

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:51:41 +02:00
Andras Bacsai 8e0f4d0dfa fix(database): prevent duplicate environment variables on start and clone
- Stop the Redis username accessor from creating REDIS_USERNAME on read
- Keep source values when cloning applications (no duplicate NIXPACKS_NODE_VERSION)
- Match exact keys when adding default database environment variables
- Write content files before database containers start
- Resolve the database proxy upstream through Docker DNS on each connection
- Remove existing duplicate REDIS_USERNAME and NIXPACKS_NODE_VERSION rows
2026-10-09 14:53:05 +02:00
Andras Bacsai f87f80ae96 feat(dns): add opt-in Cloudflare proxy mode for new DNS records 2026-10-09 13:52:20 +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
🏔️ Peak 3f953d1dce revert(git): stop forcing HTTP/1.1 for repository imports (#12080) 2026-10-08 18:10:07 +02:00
Andras Bacsai e0639a8e6d fix(applications): normalize nested base64 custom labels (#11473) 2026-10-07 16:44:59 +02:00
Andras Bacsai 81045b2dc2 fix(compose): deploy large Compose files such as Sentry self-hosted
Sentry self-hosted (getsentry/self-hosted 26.8.0) could not be deployed
as a Docker Compose application. Three separate problems blocked it:

- YAML collection aliases: the first fix raised the limit to 256 only in
  Application::oldRawParser(). The safety check added in v4.4.0
  (validateDockerComposeForInjection) and all other Compose parse calls
  still used the Symfony default of 128, so the Sentry file (133 aliases)
  failed with "Maximum number of collection aliases (128) exceeded".
  Add parseDockerComposeYaml() and use it for every Compose parse.
- Argument list too long: the deployment job sent the generated Compose
  file as base64 inside the SSH command. Linux limits one argument to
  128 KiB, and the Sentry file is about 370 KB. execute_remote_command()
  now accepts an 'input' key that streams content over SSH stdin, and
  the job writes the Compose file with it.
- A list-style build arg without a value (build.args: [SENTRY_IMAGE])
  merged into a map environment kept its numeric key, so Docker Compose
  rejected the file with "non-string key in ...environment: 0". It now
  becomes NAME with no value, as in Compose.

Closes #11725
2026-10-07 13:30:42 +02:00
Andras Bacsai 575bfaa085 feat(analytics): add configurable client IP storage modes
Allow full, anonymized, or disabled IP recording per server through the UI
and API, and pass the selected mode to Sentinel. Adapt traffic breakdowns
to the effective IP mode, including views that aggregate multiple servers.
2026-10-07 12:35:22 +02:00
Andras Bacsai 407c384b09 feat(backups): add configurable missing volume backup alerts
Check enabled volume backup schedules hourly and notify teams when no
execution occurs within the configured number of days. Expose the alert
threshold in the UI and API, with alerts disabled by default.

Route volume backup results and warnings to the appropriate Telegram
backup threads.
2026-10-07 09:37:35 +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 9e20084302 fix(auth): build email verification links from the instance URL 2026-10-06 12:56:57 +02:00
Andras Bacsai 5e92a7267c fix(cleanup): prune audit events in batches and delete old finished runner executions 2026-10-06 12:56:57 +02:00
Andras Bacsai ecf969e35d fix(audit): remove credentials from URLs before storing audit changes 2026-10-06 12:56:57 +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 Bacsai 86d84e21c0 perf(databases): load Redis-family passwords once for database lists
Connection URLs of KeyDB, Dragonfly and Redis queried their password
variables once per database, so the database list API ran extra queries
for every database.

- Read REDIS_PASSWORD and REDIS_USERNAME from the loaded relation
- Load the variables once per type in the database list and the
  environment details API
2026-10-06 06:59:22 +02: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
Andras Bacsai 7f12e3b513 fix(databases): keep the server password of existing Redis databases
v4.3.23 started Redis with the stored REDIS_PASSWORD value as it is and
unquoted, and the connection URLs used the same value. 4.4.0 resolved a
shared variable reference for the start command only, so after the first
restart the server password changed and no longer matched the URLs.

- Add legacy_password_quoting to standalone_redis; existing databases
  keep the v4.3.23 server password
- New databases resolve REDIS_PASSWORD (local, shared or remote secret)
  and quote it in the start command
- Connection URLs use the same password as the server
- Hide the flag in the API, as for KeyDB and Dragonfly
2026-10-05 22:49:36 +02:00
Andras Bacsai b08383805c fix(sentinel): prevent concurrent starts and duplicate restarts (#12100) 2026-10-05 21:56:17 +02:00
Andras Bacsai 152e06cf43 fix(databases): let REDIS_PASSWORD override the KeyDB and Dragonfly password
The stored password is the default. Before, a local REDIS_PASSWORD was
rewritten to the stored password, and a shared one replaced the stored
password, so the old password was lost.

- A local, shared, or remote secret REDIS_PASSWORD sets the server
  password; the stored password and the variable are not changed
- Removing the variable, or an empty value, uses the stored password
- Connection URLs show the password in use; for a remote secret they
  show the reference, not the secret
- Databases created before this release keep the v4.3.23 behavior
2026-10-05 21:25:57 +02:00
Andras Bacsai 8aa57d66e8 fix(previews): give each preview its own volume for external volumes
Previews of Docker Compose applications used external volumes as
written, so a preview mounted the production volume. Two database
containers on the same data can corrupt it.

- A preview uses {uuid}_<name>-pr-N with a name-only declaration, the
  same name as v4.3.23, so old preview volumes are used again
- Production still uses the external volume as written
- Preview cleanup removes the preview volume, never the external one
- Do not show the external volume warning for previews
2026-10-05 21:25:40 +02:00
Andras Bacsai 2361161487 fix(service): write large compose files through SSH stdin
Service compose and .env content was sent inline in the SSH command.
Linux limits one argument to 128 KiB, so a service with a generated
compose over about 96 KB failed to save or start with "Argument list
too long".

- Add instant_remote_write_file(), which streams content to a remote
  tee over SSH stdin (sudo tee for non-root SSH users)
- Add SshMultiplexingHelper::generateSshStdinCommand() with the same
  SSH options as generateSshCommand()
- Write the service compose and .env with the new helper
2026-10-05 17:04:05 +02:00
Andras Bacsai 88efc0716e fix: enforce resource team isolation and preserve ClickHouse data
Scope resource permissions and related records to the owning team across
team switches, cloning, deployments, and resource operations.

Merge ClickHouse volume mounts, preserve anonymous data volumes on stop,
and require adopting current data before starting or restarting.

Fix file storage deletion checks and add regression coverage.
2026-10-05 14:23:04 +02:00
Andras Bacsai 4227ec6ebf fix(storage): require deploy permission for external host writes
Require deploy or root token access when creating managed mounts outside
resource directories or changing their file contents through the API.

Synchronize KeyDB and Dragonfly passwords with local and shared
REDIS_PASSWORD values while preserving secret manager resolution.
2026-10-05 12:03:37 +02: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
Andras Bacsai b235a480b2 fix(service): detect registry-prefixed Grafana server images (#10617) 2026-10-04 13:39:19 +02:00
Andras Bacsai 9850793109 Merge remote-tracking branch 'origin/main' into pr-11788-encrypted-audit-change-details 2026-10-04 13:13:16 +02:00
Andras Bacsai 4c2253c2ad fix(previews): restrict PR loading and status updates to GitHub Apps
Add Application::isGithubAppSource(), which checks that the source is a
GithubApp, and use it in place of is_github_based() for pull request
status updates in ApplicationDeploymentJob and for the "Load pull
requests" action in the previews view.

Previews::load_prs now throws an error for non-GitHub App sources
instead of calling the GitHub API with a GitLab or other source. Also
adds a test checking that GitLab sources don't show or load GitHub pull
requests.
2026-10-04 12:29:48 +02:00