1670 Commits
Author SHA1 Message Date
Andras BacsaiandClaude Opus 5.5 b98e810e3f fix(proxy): keep Compose merge tags and remove the log rotation sidecar
- Parse and dump the proxy Compose file with custom tags, so `!reset` and
  `!override` survive the traffic analytics update and `{}`/`[]` keep
  their type.
- Remove the Traefik log rotation sidecar when the proxy stops or another
  proxy type starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:51 +02:00
Andras BacsaiandClaude Opus 5.5 1ee34a5ab2 fix(database-import): stop the restore when the import task fails or loses SSH
An import task can now run until the SSH command timeout (capped below the
Horizon worker timeout). When the CoolifyTask fails, times out, or its SSH
command ends with 124/255, Coolify stops the restore in the database
container and keeps the import in progress until the stop has run, so the
database stays blocked. The restore reports its own 124/255 as 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:29 +02:00
Andras Bacsai 50be16be8f revert(scheduler): go back to the cron scan and keep the reliability fixes
The missed daily backups on Coolify Cloud came from Redis evicting queue keys
(allkeys-lfu on a full instance), not from the scan of all schedules. The
next_run_at design (452259aac, 5c5740533) did not prevent that, and it added
stored state that must be kept in sync on every frequency, timezone, and server
change. It was not released, so remove it:
- revert next_run_at, its model hooks, the "Next run" UI, and the diagnostics
  changes; restore scheduled_job_states and the queue overlap lock
- a new migration removes the next_run_at columns and creates
  scheduled_job_states again where the removed migration already ran

Keep the changes that fix real problems:
- run the manager every minute from the scheduler with `scheduled:dispatch`,
  so a busy queue cannot delay it past the catch-up window
- load the server chain of scheduled backups with each chunk, which removes
  about 7,000 queries per run on Coolify Cloud
- catch Throwable for each schedule, so one broken schedule does not stop the
  others
- publish database and volume backups again when their queued job was not
  started after 60 minutes (for example, lost in Redis); log lost tasks and
  Docker cleanups as missed. A lost backup is not run again when a newer
  occurrence of the same schedule exists.
2026-10-02 08:05:18 +02:00
Andras Bacsai 0ed423aaac feat(webhooks): require 16 characters for new manual webhook secrets
New or changed manual webhook secrets (GitHub, GitLab, Bitbucket,
Gitea) must have at least 16 characters, in the Webhooks page and in
the application API. Stored secrets keep working: an unchanged value
passes, and an empty value still disables the webhook. Each secret
field gets a Generate button that fills a random 40-character secret;
the user saves it after adding it to the Git provider.
2026-10-01 20:31:00 +02:00
Andras Bacsai e15bd9b7e7 fix(deploy): keep the queue moving and keep healthy containers on late failures
A queued deployment whose commit no longer validates (for example one
queued before an upgrade) made the job dispatch throw. The row stayed
in_progress, blocked every later deployment of the application, and
stopped the queue loop. Such a row is now marked failed with a log
entry and a failure notification, and the next queued deployment
starts.

An exception after the new container passed its health check (for
example in additional destinations, the next-deployment loop, or
notifications) made failed() remove the new container, which could be
the only one running. failed() now keeps a healthy new container, and
errors in the steps after a status change are logged without flipping
a finished deployment to failed.
2026-10-01 20:31:00 +02:00
Andras Bacsai d1cf3e6ef1 fix(secrets): keep the secret manager source when cloning resources
Cloning an application, database, service or environment did not copy
the secret manager link, so variables with remote secret references
failed on the next deployment with "no secret manager source is
configured".

Clones now get a copy of the link when the integration token belongs to
the clone's team. Server transfer does not export integration tokens or
links; the export now warns which resources need the token and link
again on the target instance.
2026-10-01 07:16:45 +02:00
Andras Bacsai 3c23e2d30f refactor(auth): remove OAuth re-authentication for destructive actions
Drop the OAuth confirm/callback flow that let users confirm destructive
actions by re-authenticating through a linked provider. This removes the
confirm route and session handling in OauthController, the middleware
bypass for those routes, OauthSetting::providerName(),
User::oauthConfirmationProviders(),
OauthLoginService::identityBelongsToUser() and the
socialite_reauthentication_parameters() helper.

Users with a linked OAuth identity no longer need a password step and
only use the dialog's typed confirmation. Password confirmation is now
required only for users who have a password and no SSO identity. The
confirmation modal and tests are updated to match.
2026-09-30 21:37:02 +02:00
Andras Bacsai 5a2006e99c perf(docker): look up foreign preview owners once per container status check
GetContainersStatus now resolves all unknown preview owners with one query,
like the Sentinel push. The lookup helper also no longer fails on an
application without a uuid.
2026-09-30 19:23:35 +02:00
Andras Bacsai 7183e52779 fix(deploy): never force start the same deployment twice
Force start now uses the locked start path. It still skips the concurrency
limits, but a deployment that is no longer queued is not started again, and
the deployment page says so.
2026-09-30 19:23:35 +02:00
Andras Bacsai 41eb524d3e fix(dns): compare IPv6 addresses by value 2026-09-30 18:10:10 +02:00
Andras Bacsai 6dc332c203 perf(sentinel): cache traffic per minute, skip unsafe keys, batch owner lookups
- Align the traffic window to the minute so the 60 s cache is used.
- Skip only an unsafe key instead of hiding the whole server.
- Look up unknown container owners in one query per push.
2026-09-30 18:10:10 +02:00
Andras Bacsai f8f2945aec fix(deploy): start queued deployments atomically and report cancelled multi-server runs
- Decide whether to start a queued deployment inside the application and
  server lock, so two requests cannot start the same app twice.
- Send the multi-server summary when the last child is cancelled.
- Show the registry message when an inline Dockerfile image is missing on a
  deployments-only additional server.
2026-09-30 18:10:10 +02:00
Andras Bacsai 5921fb2958 fix(compose): keep name-only declaration for pre-existing volumes
Before Coolify kept `driver`, `driver_opts` and `labels` of renamed
Compose volumes, Docker created those volumes without them. Applying the
options now would make Docker Compose ask to recreate the volumes on
every deployment.

- Add `ignores_compose_driver_options` column to local_persistent_volumes
  and mark all volumes that exist at upgrade time
- Parsers now use composeRenamedVolumeDeclarationFor() so flagged volumes
  keep the old name-only top-level declaration
- Legacy application parsers (v1/v2) always emit name-only declarations
- Reset the flag when a volume is replicated, since a clone is a new
  Docker volume
- Include the flag in server transfer export/import; bundles without it
  default to true
- Storages UI shows which volumes ignore driver options and allows
  deleting them so the options apply on recreation
- Add tests for parsers, cloning and server transfer
2026-09-30 14:31:33 +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
Andras Bacsai d1bdecba6d fix(compose): allow variables in external volume names
Accept Compose variables ($NAME, ${NAME}, ${NAME-default},
${NAME:-default}) in the `name:` of an external volume, and in the old
`external: {name: x}` syntax. Docker Compose resolves them from the
deployment .env. Coolify never removes external volumes, so it does not
need the resolved name.

The volume key must still be a literal Docker volume name, because
Compose does not resolve variables in keys. Names that are unsafe or use
unsupported syntax are still rejected, for example command substitution,
spaces, escaped dollars, and ${VAR:?err}.

Add tests for application and service parsing, legacy volume rows, the
StackForm save, legacy Compose parsing, and the rejected cases.
2026-09-30 12:29:50 +02:00
Andras Bacsai 033f78d907 fix(backup): fail compressed dumps when the dump command fails
A plain pipe into the compressor only reported the compressor's exit
status, so a failed dump still produced a small, valid archive. Add
pipeToFileKeepingExitStatus() to propagate the source's exit status in
POSIX sh and use it for all compressed dump commands.

- Delete the partial local backup file on any failed backup, not only
  for ClickHouse, since untracked files are never removed by retention
- Wait up to 10s for locks when running VACUUM INTO for SQLite backups
- Refuse SQLite restores unless the file has a SQLite header, so an
  empty backup no longer wipes the database
- Add tests for dump exit status handling and SQLite restore validation
2026-09-29 23:23:02 +02:00
Andras Bacsai 2ac5969d90 fix(docker): stop matching containers by numeric resource id labels
Numeric coolify.{type}Id labels can collide with resources on another
Coolify instance managing the same server, so they no longer select or
resolve container owners. Drop the legacyId fallback from container
lookups in DeleteService and ComplexStatusCheck.

Legacy application containers without a UUID label are now resolved by
their compose service name, which starts with the application UUID.
2026-09-29 23:05:34 +02:00
Andras Bacsai 680f4e5d29 fix: use GitLab API v4 for OAuth and gate env copy by update policy
Socialite's GitLab provider fetches the user from /api/v3/user, which
GitLab removed in 11.0 (410 Gone). Add App\Auth\GitlabProvider that
reads /api/v4/user with a bearer token and register it in the
socialite helper, for both gitlab.com and self-hosted base URLs.

Environment variable copyValue now checks the 'update' policy on the
variable instead of the member role of the current team, so members
can no longer copy values after switching to a team they own. Remove
the redundant member check from ShowHardcoded.

Add tests for the GitLab v4 user lookup and the team-switch copy case.
2026-09-29 22:36:56 +02:00
Andras Bacsai a599c9e8b7 fix(server): keep data directory commands working for non-root SSH users
A non-root SSH user cannot open redirects, expand globs, or cd into
/data/coolify (9999:root 0700). Several remote commands relied on that
and failed on non-root servers.

- Write README files with `tee` instead of shell redirects in database
  starts and application deployments
- Clear Postgres init scripts with `find` instead of a glob
- Split the proxy config backfill if/else so each branch gets sudo
- Start the log drain with `docker compose -f` instead of `cd`
- Wrap the preserved-repository compose start in `sh -c` for non-root
- Stage clone archives outside /data/coolify via StagesCloneArchives
- Extend the sudo parser and add non-root command tests
2026-09-29 20:46:54 +02:00
Andras Bacsai 452259aacc feat(scheduler): dispatch scheduled jobs by their stored next run time
The scheduled job manager checked every enabled schedule each minute and
evaluated its cron expression in PHP. On Coolify Cloud one run took more than
60 seconds, so runs overlapped and minutes were skipped.

Each schedule now stores its next due time:
- next_run_at on scheduled database backups, scheduled tasks, and volume
  backups, and docker_cleanup_next_run_at on server settings (UTC, indexed)
- model hooks calculate it when a schedule is created or its frequency or
  enabled flag changes; a server timezone change or a move to another server
  calculates it again for the affected schedules

The `scheduled:dispatch` command runs the manager every minute from the
scheduler, not from a queue. It selects only due rows and claims each
occurrence with an atomic update of next_run_at, together with its delivery
record, so parallel runs on several nodes never dispatch it twice.
- database and volume backups run once when they are late; tasks and Docker
  cleanups more than 10 minutes late are logged as missed
- a local time that occurs two times when the clocks go back runs once
- the migration continues each schedule after its last recorded occurrence and
  drops scheduled_job_states

Show the next run, in the server timezone, on the database backup, scheduled
task, volume backup, and Docker cleanup pages, or "Invalid frequency" when the
frequency cannot be calculated. scheduled:diagnostics now lists overdue
schedules and open deliveries.

Remove workarounds that are no longer needed: the queue overlap lock and its
stale-lock repair, the catch-up window, and debug-only logs.
2026-09-29 10:07:23 +02:00
Andras Bacsai af983a29d8 refactor(docker): identify containers by resource UUID instead of numeric id
Container ownership labels now use UUIDs: coolify.applicationUuid,
coolify.serviceUuid, coolify.service.subUuid and coolify.databaseUuid.
Numeric ids differ between Coolify instances, so after a server transfer
the target could not find existing containers: redeploys left the old
container running and service statuses were wrong.

Containers are matched by UUID label first, then by the compose project
(Swarm: stack namespace), which is the resource UUID since July 2024, and
last by the older numeric id label. Existing containers keep working
without a redeploy and get the UUID labels on the next deploy or restart.
2026-09-29 09:57:56 +02:00
Andras BacsaiandClaude Opus 5.5 7bd8baa7f0 fix(multi-server): make applications on several servers consistent
- Stop the application on every server, also when one server is not functional.
- Write file mounts on every server of the application, not only on the main server.
- Deploy the exact commit, rollback flag and image tag of the main deployment to the
  additional servers, so a rollback reverts all servers.
- Pull the pushed image on additional servers for inline Dockerfile applications
  instead of building it again on each server.
- Fail multi-server deployments without a Docker image name, also from webhooks and the API.
- Send one deployment notification after all servers finish (parent_deployment_uuid).
- Block persistent volumes on multi-server applications (API, Livewire, SQLite connect)
  and warn in the deployment log for existing applications that have both.
- Remove dead code in push_to_docker_registry().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 15:38:57 +02:00
Andras BacsaiandClaude Opus 5.5 7c21c330c9 fix(gitlab): check the private key before using it for git commands
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 14:06:14 +02:00
Andras BacsaiandClaude Opus 5.5 f540c49f68 fix(security): encrypt stored remote commands and remove them after the final attempt
remote_process() stored the full shell command as plain text in
activity_log and kept it for 60 days. Database starts store the compose file
with the database passwords, imports store S3 keys, and the Cloudflare
tunnel setup stores its token.

- Store the command encrypted. The queue worker decrypts it right before the
  SSH call and still reads plain commands of tasks queued before the upgrade.
- Remove the command when the task is finished: after a successful run, in
  failed() when no attempt is left, and for a stopped import. A failed attempt
  that the queue retries keeps it.
- Do not write a command preview into the error log.
- Migration: remove stored commands from activities that no longer run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:41:56 +02:00
Andras BacsaiandClaude Opus 5.5 f8b320ab40 fix(servers): validate the selected private key when adding a server
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:35:46 +02:00
Andras BacsaiandClaude Opus 5.5 eed50d6781 fix(github): check the private key before generating app tokens
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:18:29 +02:00
Andras BacsaiandClaude Opus 5.5 9c7f2dc412 fix(templates): keep local service templates when the CDN bundle is unusable
A 200 response with an empty body or an error page replaced the local
templates and the shared cache, so the service list became empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:13:20 +02:00
Andras BacsaiandClaude Opus 5.5 a6a3b1d26b revert: undo the app changes from the test clean-up
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>
2026-09-28 08:06:34 +02:00
Andras BacsaiandClaude Opus 5.5 1e66c9f9bd fix(templates): keep local service templates when the CDN bundle is unusable
A 200 response with an empty body or an error page replaced the local
templates and the shared cache, so the service list became empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:18:09 +02:00
Andras BacsaiandClaude Opus 5.5 862e021f2d feat(compose): warn about legacy external volumes and list external volumes
Legacy Compose applications keep the old name of an external volume. The
deployment log now tells the user why and how to move to the external
volume. The storage page shows external volumes as a read-only list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:09:13 +02:00
peaklabs-dev 2aef5ab459 feat(database): let docker compose applications mount the sqlite volume 2026-09-27 18:00:34 +02:00
Andras BacsaiandClaude Opus 5.5 49ae227f6e fix(compose): keep the old volume of existing resources for external volumes
Switching an external volume from the old <uuid>_<name> volume to the
declared external volume on redeploy could make an app look as if it
lost its data (for example a database that initializes a new, empty
data directory).

- If a resource already has the storage entry with the old generated
  name, the parsers keep using that volume and show a warning in the
  deployment log and the service start log, with the steps to switch.
- Without that entry (new resources, or external volumes added later)
  the external volume is used as declared.
- Deleting the old storage entry (storage page) switches to the
  external volume on the next deployment; the storage entry shows a
  note about it.
- Legacy Compose applications (parser versions 1 and 2) never stored
  their volumes, so they keep the old volume names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 17:24:51 +02:00
Andras BacsaiandClaude Opus 5.5 ee62f64add fix(compose): use external volumes as declared
Compose volumes declared with external: true (or external: {name: x})
were renamed to <uuid>_<name>, so containers got a new empty volume
instead of the existing one. All Compose parsers (applications,
previews, services, and the legacy parsers) now use them as written:
no uuid prefix, no preview suffix, the top-level declaration kept, and
no tracking row, so Coolify never removes a shared external volume.
External volume names must be literal; ${VAR} names are rejected.
Existing <uuid>_<name> volumes and their rows are kept on redeploy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:53:54 +02:00
Andras BacsaiandClaude Opus 5.5 fe6854c73e fix(storage): write content files before compose up and never drop their content
- Compose content files are written by a queued job. Deployments and
  service starts now check all content files with one command before
  docker compose up: a missing file or an empty directory (left by
  Docker) is written; an existing file is kept; a directory with files
  is kept with a visible warning.
- The server file sync that runs after every parse marked a file
  storage as a directory and deleted its configured content when a
  directory was at its path. A storage with content now keeps its
  content; an empty directory is replaced with the file.
- Saving a storage no longer fails for an existing file, replaces an
  empty directory, and no longer deletes a directory with files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 16:26:56 +02:00
Andras BacsaiandClaude Opus 5.5 700d476bac fix(dev): rewrite host paths only for the testing-host dev server
Development mode rewrote /data/coolify paths to the dev Docker volume
for every server. That is only right for the testing-host backend,
which uses the host Docker daemon. The KVM localhost and other dev VMs
have their own /data/coolify, so bind mounts, Compose content files,
the Traefik and Caddy traffic mounts, Sentinel, and database proxies
pointed to wrong paths there.

- Server::sharesDevHostDocker() and devHostDockerPath() decide per
  server; only the testing-host server (ip coolify-testing-host) gets
  the volume path. Production is unchanged.
- The testing-host volume names come from the Compose project
  (DEV_COOLIFY_DATA_VOLUME, DEV_COOLIFY_BACKUPS_VOLUME), with the
  legacy names as fallback.
- In development, saved proxy configurations with the old path are
  fixed when they load and ask for a proxy restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 15:21:39 +02:00
Andras BacsaiandClaude Opus 5.5 aabd127374 fix(compose): keep content files inside the resource directory
- A Compose bind volume with a Coolify `content:` block could make
  Coolify write any host file (for example /root/.ssh/authorized_keys)
  when the file was loaded. Content sources must now be inside the
  resource directory (./ paths); other sources get a clear validation
  error for services and applications (UI, API, load, deployment),
  and every content write is confined again on the server. Bind
  mounts without content are unchanged. All 94 template content
  volumes use ./ paths and still work.
- Quote container names in scheduled tasks and in the stop actions for
  applications, previews, and services.
- Fix a development-only crash when a preview with a bind mount was
  parsed (the preview suffix returned a plain string).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 14:37:21 +02:00
Andras BacsaiandClaude Opus 5.5 5b2724621a fix(compose): validate Git-based Docker Compose applications
Git-based Docker Compose applications skipped the Compose injection
validation that services use. It now runs when the file is loaded or
reloaded, when the raw Compose is saved (UI and API create), and at
deployment before any command uses the file; an unsafe file is not
saved, and a deployment stops with a clear log line.

- All 371 service templates and realistic Compose files still pass.
- Network names may now mix text with $VAR, ${VAR}, ${VAR:-default},
  or ${VAR-default} (for example ${COMPOSE_PROJECT_NAME}_default), so
  such existing applications keep deploying; command substitution and
  unsafe defaults stay rejected.
- Quote the preserved-repository path in a stat command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:47:01 +02:00
peaklabs-dev a52cafb2db Merge branch 'main' into feat/add-sqlite-database 2026-09-26 23:22:49 +02:00
Andras BacsaiandClaude Opus 5.5 45f7b8422f fix(compose): create network name variables for Compose applications
A Docker Compose application could use ${VAR} as a network name, but
only services created the variable. With ${VAR:-default} the value was
hidden and could not be edited; with ${VAR} Compose got an empty name
and the deployment failed. Applications now get the variable (and its
preview copy) with the default from the compose file, like services.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 22:18:17 +02:00
🏔️ Peak edcb884266 Merge branch 'main' into feat/add-sqlite-database 2026-09-26 21:32:43 +02:00
Andras BacsaiandClaude Opus 5.5 dc20ffd92b fix(proxy): warn about old Caddy images and share the Caddy traffic log path
- Servers with caddy-docker-proxy older than 2.9 show a warning on the
  Proxy page and the traffic analytics settings, with the fix.
- Caddy 2.9+ images get the basic_auth label; older ones keep the
  deprecated basicauth, which Caddy 2.7 still needs.
- Caddy mounts StartSentinel::trafficLogDirectory() as /traffic, so
  Caddy and Sentinel use the same access log, also in development.
- The development data volume is configurable (DEV_COOLIFY_DATA_VOLUME,
  same default as before) and shared by Traefik, Caddy, and Sentinel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:19:14 +02:00
Andras BacsaiandClaude Opus 5.5 a8117f9114 fix(databases): unblock interrupted imports and prevent double starts
- A Coolify restart now fails queued or running database imports, so
  they no longer block all later imports with 409.
- An import without progress for the SSH command timeout plus 30
  minutes (at least 2 hours) is stale and no longer blocks.
- The import sets its operation property when the activity is created,
  so a worker cannot save the activity without it.
- Start and restart refuse a second operation while a start, restart,
  or import is in progress, in the UI, the API (409), and actions.
- DatabaseStartJob runs only while its activity is still queued and
  the newest one, and holds a lock per database during the commands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 10:40:53 +02:00
Andras BacsaiandClaude Opus 5.5 510a2d838b feat(analytics): log Compose resources on Caddy and add service analytics
Traffic analytics now covers every resource on both proxies:

- Caddy: Compose applications and services get access-log labels. The
  log_append key is {uuid}-{service}, the same key Sentinel reads from
  Traefik router names.
- A resource owns the Sentinel key {uuid} and every key that starts
  with {uuid}-. Application pages and the traffic card now merge all
  keys, so Compose applications and previews show data on Traefik too.
- Global analytics: the leaderboard groups the keys of one resource
  into one row, lists services, and never names keys of other teams.
- New service analytics page (project.service.analytics) with the same
  view and authorization as the application page.
- Switching the proxy type restarts Sentinel when analytics is on, so
  it reads the new log path. Sentinel gets no traffic mount when the
  proxy has no analytics support.
- Caddy servers show a note that resources log only after a redeploy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:00:55 +02:00
Andras BacsaiandClaude Opus 5.5 f92ee342a1 fix(proxy): remove legacy Traefik dashboard labels from saved configs
Older Coolify versions saved Traefik configs with a router that sends
requests with the proxy container name as Host header (for example
traefik-coolify-proxy) on port 80 to api@internal. New installs no
longer get these labels, but saved configs keep them after an upgrade.

- Replace only the unchanged legacy labels with traefik.enable=false,
  keep comments and formatting, and skip routers the user customized.
- A migration and GetProxyConfiguration fix the saved config in the
  database only. The proxy is not restarted; the UI asks for a restart.
- Change the pending notice to "Your configuration changed, please
  restart the proxy."
- Resolve conflict markers committed in the Server Proxy component,
  which broke the proxy page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 23:18:34 +02:00
Andras Bacsai 165d2f3dc8 fix: harden traffic analytics, terminal errors and Postgres restores
Traffic analytics:
- Reject enabling unless the server runs a Coolify-managed Traefik or
  Caddy proxy, and show the reason in the settings UI
- Save the proxy configuration before the setting so a failure leaves
  both unchanged; allow disabling after the proxy was removed
- Skip the proxy restart when the proxy is stopped and report that the
  config applies on next start
- Preserve the user's own Traefik --accesslog* flags and restore them
  when analytics is disabled
- Rotate the Traefik access log in the sidecar with BusyBox tools
  (copytruncate, 5 gzip rotations)
- Create the access log before starting Sentinel, which opens it once

Terminal:
- Detect and surface WebSocket connection rejections in the browser
  terminal, with shared helpers in terminal-connection.js and
  terminal-utils.js

Database import:
- Restore PostgreSQL backups in a single transaction; SQL replace
  restores go into a temporary database and swap in only on success,
  leaving the current database untouched on failure
2026-09-25 22:58:07 +02:00
peaklabs-dev 5261785c69 Merge remote-tracking branch 'origin/next' into feat/add-sqlite-database 2026-09-25 22:47:29 +02:00
Andras Bacsai 37bd776f70 feat(services): allow variables in compose network names
Accept a top-level network `name:` that is a single Compose variable
(`${VAR}`, `${VAR:-default}`, `${VAR-default}`), e.g. for external
networks that differ per server. The default must still be a valid
network name; surrounding text, nested variables, and shell syntax
are still rejected.

When parsing services, such variables are created as service
environment variables (seeded with the default) so users can view and
change them, while the compose file keeps the variable for Compose to
resolve from .env at deployment. Existing values are preserved on
re-parse.
2026-09-25 21:43:35 +02:00
Andras Bacsai 84b596f7ca fix(proxy): only emit Caddy log_append on caddy-docker-proxy 2.9+
Caddy 2.7.6, shipped in the caddy-docker-proxy 2.8 image, rejects the
whole Caddyfile when it contains log_append. Add
Server::caddySupportsLogAppend(), which reads the image from the applied
proxy configuration. Traffic analytics labels now add log_append only
when the server runs 2.9 or newer and has no pending proxy change.

- Change the default Caddy proxy image from 2.8-alpine to 2.13-alpine
- ProxyPortParser now validates Docker Compose port ranges and random
  host ports. It returns only fixed host ports for the availability
  check and has a clearer validation message
- After mkdir, chown only root-owned files and remove other-user access
  from the top directory only. Files owned by container users and the
  modes of mounted files no longer change
- Add tests for log_append support and the new parser/sudo behaviour
- Note in the lessons file that tests must flush the Server identity map
  between dataset cases
2026-09-25 13:59:30 +02:00
Andras Bacsai 82bb574760 fix(servers): stop double sudo on chown/chmod after mkdir
The mkdir ownership rule prepended `sudo` to chown/chmod, and the &&
rule then added another, producing `sudo sudo`. That fails on hosts
like Alpine, where root is not in sudoers. Both parseCommandsByLineForSudo
and parseLineForSudo now leave the prefix to the && rule.

Also:
- Fix the `App\Helpers\SSLHelper` reference in RegenerateSslCertJob to
  `SslHelper`, so it autoloads on case-sensitive filesystems.
- Stop StartPostgresql and StartMongodb from overriding the configuration
  dir with a hardcoded Docker volume path in development.
- Add tests for nested sudo behaviour, the class reference letter case
  and database configuration dirs in development.
2026-09-25 13:15:57 +02:00
Andras Bacsai 0935bf141e Improve volume path handling (#11993) 2026-09-24 23:22:35 +02:00