65 Commits
Author SHA1 Message Date
Andras BacsaiandClaude Opus 5.5 65a7bd5c05 chore: merge main into release/v4.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 13:37:20 +02:00
Andras Bacsai d55852ee94 fix(proxy): persist running status from Sentinel push updates (#12237) 2026-10-20 10:49:26 +02:00
22a8c09dd6 Next (#12202)
Co-authored-by: Aditya Tripathi <aditya@climactic.co>
Co-authored-by: peaklabs-dev <122374094+peaklabs-dev@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: rosslh <ross@rosshill.ca>
Co-authored-by: Abdulfetah Suudi <suudiabdulfetah@gmail.com>
Co-authored-by: Dharmveer <51308779+dharmveer97@users.noreply.github.com>
Co-authored-by: Maxwell <136101+mxswd@users.noreply.github.com>
2026-10-19 10:07:23 +02:00
Andras Bacsai ed6d6b1e45 fix(server): reset disk alerts only after usage recovers
Require usage to fall five percentage points below the threshold before releasing the notification throttle. Reset it after Sentinel reports recovery so a new spike alerts immediately.

Add regression coverage for recovery and usage fluctuating near the threshold.
2026-10-05 23:14:52 +02:00
Andras Bacsai f63bf5abaa fix(sentinel): refresh UI on container health and status changes
Include health status in push deduplication and broadcast ServiceChecked after processing complete or partial snapshots.
2026-10-04 16:25:00 +02:00
Andras Bacsai 22900b6d12 fix(proxy): stop automatic proxy network reconnects on status checks
Remove the periodic ConnectProxyToNetworksJob dispatch from
PushServerUpdateJob, the synchronous connect in ServerCheckJob when the
proxy is found running, and the connect-on-add in the server
Destinations component. Drop the now-unused
proxy.connect_networks_interval_seconds config option.

Replace the rate-limit/interval tests with a test asserting that no
jobs are pushed when the proxy is running.
2026-10-04 00:36:28 +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 99c840922c fix(notifications): throttle repeated notifications per resource
High disk usage notifications were sent every ~10 minutes on Coolify Cloud
because the cache rate limiter did not hold across Sentinel-triggered checks.

- Add a notification_throttles table and NotificationThrottle helper that
  atomically records when a notification was sent for a resource.
- Add the ThrottledNotification contract; Team::notify() skips a throttled
  notification that was already sent within its interval.
- High disk usage: configurable per-server interval
  (server_disk_usage_notification_interval_hours, default 24h) in
  Server > Advanced and the servers API.
- Container restarted (coolify-proxy, database TCP proxy): at most one
  notification per hour per server/database during a restart loop.
- Move the unreachable and missing-backup markers into the new table and
  migrate existing data (servers.unreachable_notification_sent and
  scheduled_database_backups.missing_backup_notification_sent_at).
- Regenerate OpenAPI files.
2026-09-29 11:29:39 +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
peaklabs-dev 8e61ace98d feat(database): add sqlite start, stop, restart and delete support 2026-09-24 19:03:42 +02:00
Andras Bacsai 053b030c42 fix: remove database restart limits and clear stale Traefik state
Drop restart-limit fields and enforcement from database resources, clear cached Traefik version data when proxies change, and show missing service environment variables from disabled deploy actions.
2026-09-04 15:42:26 +02:00
Andras Bacsai 67b33c27a9 fix(applications): persist restart limit state for stopped containers (#11566) 2026-08-31 13:13:26 +02:00
Andras Bacsai e28d8ca0ff fix: handle complete empty snapshots and match dropdown trigger width 2026-08-16 18:26:40 +02:00
Andras Bacsai e01b8a057e fix(servers): retain cloud instances awaiting IPs
Persist DigitalOcean, Hetzner, and Vultr servers before public IP
assignment, then backfill placeholder addresses from provider state.

Treat partial Sentinel snapshots as non-authoritative and document the
destinations API with OpenAPI schemas.
2026-07-11 21:35:10 +02:00
Andras Bacsai 20f9bb4305 perf(realtime): reduce push update churn
Cache destination lookups and skip empty resource queries during push
server updates. Add database indexes and Postgres storage tuning for
hot-update tables, and make the realtime entrypoint forward process
failures and signals reliably.
2026-05-27 19:38:23 +02:00
Andras Bacsai 626cfb4a22 fix(sentinel): reduce resource churn from health flaps
Ignore health status changes in Sentinel push deduplication when the container lifecycle state is unchanged.

Scope stale resource checks to Sentinel servers whose heartbeat is stale, and avoid refreshing resource last_online_at on unchanged statuses.
2026-05-27 16:48:38 +02:00
Andras Bacsai 097efd14ce fix(storage): clear stale disk usage cache
Forget cached storage threshold state when reported disk usage drops below the alert threshold, allowing future threshold crossings to dispatch a fresh storage check.
2026-05-26 14:45:49 +02:00
Andras Bacsai 36526928df feat(sentinel): deduplicate metrics push processing
Move Sentinel push handling into a controller and dispatch server update jobs only when container state changes or the force interval elapses. Add opt-in PostgreSQL read/write replica configuration and tune periodic proxy network and storage checks to reduce unnecessary work.

Add feature coverage for replica config, Sentinel push deduplication, deployment log scrolling, and server update job optimizations.
2026-05-22 12:48:48 +02:00
Andras Bacsai 458f048c4e fix(push-server): track last_online_at and reset database restart state
- Update last_online_at timestamp when resource status is confirmed active
- Reset restart_count, last_restart_at, and last_restart_type when marking database as exited
- Remove unused updateServiceSubStatus() method
2026-03-10 21:46:26 +01:00
Andras Bacsai e41dbde46b chore: prepare for PR 2026-03-10 18:34:37 +01:00
Andras Bacsai f68793ed69 feat(jobs): optimize async job dispatches and enhance Stripe subscription sync
Reduce unnecessary job queue pressure and improve subscription sync reliability:

- Cache ServerStorageCheckJob dispatch to only trigger on disk percentage changes
- Rate-limit ConnectProxyToNetworksJob to maximum once per 10 minutes
- Add progress callback support to SyncStripeSubscriptionsJob for UI feedback
- Implement bulk fetching of valid Stripe subscription IDs for efficiency
- Detect and report resubscribed users (same email, different customer ID)
- Fix CleanupUnreachableServers query operator (>= 3 instead of = 3)
- Improve empty subId validation in PushServerUpdateJob
- Optimize relationship access by using properties instead of query methods
- Add comprehensive test coverage for all optimizations
2026-02-28 13:18:44 +01:00
Andras Bacsai e9323e3550 chore: prepare for PR 2026-02-15 13:43:08 +01:00
Andras Bacsai fc229c4889 chore: prepare for PR 2026-02-03 15:32:03 +01:00
Andras BacsaiandClaude Haiku 4.5 6d47d24169 Fix standalone database "restarting" status flickering and add restart tracking
- Fix status flickering: Track databases in active/transient states (restarting, starting, created, paused) not just running
- Add isActiveOrTransient() helper to distinguish between active states and terminal states (exited, dead)
- Add safeguard: Protect updateNotFoundDatabaseStatus() from marking as exited when containers collection is empty
- Add restart_count tracking: New migration adds restart_count, last_restart_at, last_restart_type to all standalone database tables
- Update 8 database models with $casts for new restart tracking fields
- Update GetContainersStatus to extract RestartCount from Docker and update database models
- Reset restart tracking when database exits completely

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-17 16:25:41 +01:00
Andras BacsaiandClaude Haiku 4.5 0efa4af5c3 Optimize PushServerUpdateJob performance with batch updates and async jobs
- Eager load service applications and databases to eliminate N+1 queries
- Replace individual model updates with batch database updates for applications, previews, and services
- Move connectProxyToNetworks to async ConnectProxyToNetworksJob to avoid blocking status updates
- Optimize Server.databases() and applications() methods with efficient database queries
- Use flatMap for cleaner collection transformations

🤖 Generated with Claude Code

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-15 14:06:32 +01:00
Andras BacsaiandClaude 66e81d6d96 Fix container status display: preserve "Restarting" for applications and sub-resources
Add preserveRestarting parameter to ContainerStatusAggregator to allow applications
and service sub-resources to display "Restarting" status instead of being marked as
"Degraded". This gives better visibility into container restart behavior.

- Update ContainerStatusAggregator to accept preserveRestarting parameter (defaults to false)
- Update GetContainersStatus to use preserveRestarting: true for applications and service sub-resources
- Update PushServerUpdateJob to use preserveRestarting: true for applications and service sub-resources
- Add comprehensive documentation explaining the parameter behavior and when to use it

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 08:23:35 +01:00
Andras Bacsai e0dc12678b fix: comprehensive SERVICE_URL/SERVICE_FQDN handling improvements and queue reliability fixes (#7275) 2025-11-24 11:47:11 +01:00
Andras BacsaiandClaude ac9eca3c05 fix: don't show health status for exited containers
Exited containers don't run health checks, so showing "(unhealthy)" is
misleading. This fix ensures exited status displays without health
suffixes across all monitoring systems (SSH, Sentinel, services, etc.)
and at the UI layer for backward compatibility with existing data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 09:09:37 +01:00
Andras Bacsai 85b73a8c00 fix: initialize Collection properties to handle queue deserialization edge cases 2025-11-21 12:25:25 +01:00
Andras Bacsai ae6eef3cdb feat(tests): add comprehensive tests for ContainerStatusAggregator and serverStatus accessor
- Introduced tests for ContainerStatusAggregator to validate status aggregation logic across various container states.
- Implemented tests to ensure serverStatus accessor correctly checks server infrastructure health without being affected by container status.
- Updated ExcludeFromHealthCheckTest to verify excluded status handling in various components.
- Removed obsolete PushServerUpdateJobStatusAggregationTest as its functionality is covered elsewhere.
- Updated version number for sentinel to 0.0.17 in versions.json.
2025-11-20 17:31:07 +01:00
Andras BacsaiandClaude 14bba8ba86 fix: correct Sentinel default health status and remove debug logging
This commit addresses container status reporting issues and removes debug logging:

**Primary Fix:**
- Changed PushServerUpdateJob to default to 'unknown' instead of 'unhealthy' when health_status field is missing from Sentinel data
- This ensures containers WITHOUT healthcheck defined are correctly reported as "unknown" not "unhealthy"
- Matches SSH path behavior (GetContainersStatus) which already defaulted to 'unknown'

**Service Multi-Container Aggregation:**
- Implemented service container status aggregation (same pattern as applications)
- Added serviceContainerStatuses collection to both Sentinel and SSH paths
- Services now aggregate status using priority: unhealthy > unknown > healthy
- Prevents race conditions where last-processed container would win

**Debug Logging Cleanup:**
- Removed all [STATUS-DEBUG] logging statements (25 total)
- Removed all ray() debugging calls (3 total)
- Removed proof_unknown_preserved and health_status_was_null debug fields
- Code is now production-ready

**Test Coverage:**
- Added 2 new tests for Sentinel default health status behavior
- Added 5 new tests for service aggregation in SSH path
- All 16 tests pass (66 assertions)

**Note:** The root cause was identified as Sentinel (Go binary) also defaulting to "unhealthy". That will need a separate fix in the Sentinel codebase.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 11:10:34 +01:00
Andras BacsaiandClaude 747a48b933 debug: add detailed Sentinel container processing logging
Added comprehensive logging to track why applicationContainerStatuses
collection is empty in PushServerUpdateJob.

## Logging Added

### 1. Raw Sentinel Data (line 113-118)
**Logs**: Complete container data received from Sentinel
**Purpose**: See exactly what Sentinel is sending
**Data**: Container count and full container array with all labels

### 2. Container Processing Loop (line 157-163)
**Logs**: Every container as it's being processed
**Purpose**: Track which containers enter the processing loop
**Data**: Container name, status, all labels, coolify.managed flag

### 3. Skipped Containers - Not Managed (line 165-171)
**Logs**: Containers without coolify.managed label
**Purpose**: Identify containers being filtered out early
**Data**: Container name

### 4. Successful Container Addition (line 193-198)
**Logs**: When container is successfully added to applicationContainerStatuses
**Purpose**: Confirm containers ARE being processed
**Data**: Application ID, container name, container status

### 5. Missing com.docker.compose.service Label (line 200-206)
**Logs**: Containers skipped due to missing com.docker.compose.service
**Purpose**: Identify the most likely root cause
**Data**: Container name, application ID, all labels

## Why This Matters

User reported applicationContainerStatuses is empty (`[]`) even though
Sentinel is pushing updates. This logging will reveal:

1. Is Sentinel sending containers at all?
2. Are containers filtered by coolify.managed check?
3. Is com.docker.compose.service label missing? (most likely)
4. What labels IS Sentinel actually sending?

## Expected Findings

Based on investigation, the issue is likely:
- Sentinel is NOT sending com.docker.compose.service in labels
- Or Sentinel uses a different label format/name
- Containers pass all other checks but fail on line 190-206

## Next Steps

After logs appear, we'll see exactly which filter is blocking containers
and can fix the root cause (likely need to extract com.docker.compose.service
from container name or use a different label source).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 08:34:42 +01:00
Andras BacsaiandClaude d2d9c1b2bc debug: add comprehensive status change logging
Added detailed debug logging to all status update paths to help
diagnose why "unhealthy" status appears in the UI.

## Logging Added

### 1. PushServerUpdateJob (Sentinel updates)
**Location**: Lines 303-315
**Logs**: Status changes from Sentinel push updates
**Data tracked**:
- Old vs new status
- Container statuses that led to aggregation
- Status flags (hasRunning, hasUnhealthy, hasUnknown)

### 2. GetContainersStatus (SSH updates)
**Location**: Lines 441-449, 346-354, 358-365
**Logs**: Status changes from SSH-based checks
**Scenarios**:
- Normal status aggregation
- Recently restarted containers (kept as degraded)
- Applications not running (set to exited)
**Data tracked**:
- Old vs new status
- Container statuses
- Restart count and timing
- Whether containers exist

### 3. Application Model Status Accessor
**Location**: Lines 706-712, 726-732
**Logs**: When status is set without explicit health information
**Issue**: Highlights cases where health defaults to "unhealthy"
**Data tracked**:
- Raw value passed to setter
- Final result after default applied

## How to Use

### Enable Debug Logging
Edit `.env` or `config/logging.php` to set log level to debug:
```
LOG_LEVEL=debug
```

### Monitor Logs
```bash
tail -f storage/logs/laravel.log | grep STATUS-DEBUG
```

### Log Format
All logs use `[STATUS-DEBUG]` prefix for easy filtering:
```
[2025-11-19 13:00:00] local.DEBUG: [STATUS-DEBUG] Sentinel status change
{
  "source": "PushServerUpdateJob",
  "app_id": 123,
  "app_name": "my-app",
  "old_status": "running:unknown",
  "new_status": "running:healthy",
  "container_statuses": [...],
  "flags": {...}
}
```

## What to Look For

1. **Default to unhealthy**: Check Application model accessor logs
2. **Status flipping**: Compare timestamps between Sentinel and SSH updates
3. **Incorrect aggregation**: Check flags and container_statuses
4. **Stale database values**: Check if old_status persists across multiple logs

## Next Steps

After gathering logs, we can:
1. Identify the exact source of "unhealthy" status
2. Determine if it's a default issue, aggregation bug, or timing problem
3. Apply targeted fix based on evidence

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 13:52:08 +01:00
Andras BacsaiandClaude 6b62847a11 fix: preserve unknown health status in Sentinel updates (PushServerUpdateJob)
## Problem
Services with "running (unknown)" status were periodically changing
to "running (healthy)" every ~30 seconds when Sentinel pushed updates.
This was confusing for users and inconsistent with SSH-based status checks.

## Root Cause
`PushServerUpdateJob::aggregateMultiContainerStatuses()` was missing
logic to track "unknown" health state. It only tracked "unhealthy" and
defaulted everything else to "healthy".

When Sentinel pushed updates with "running (unknown)" containers:
- The job saw `hasRunning = true` and `hasUnhealthy = false`
- It incorrectly returned "running (healthy)" instead of "running (unknown)"

## Solution
Updated `PushServerUpdateJob` to match the logic in `GetContainersStatus`:

1. Added `$hasUnknown` tracking variable
2. Check for "unknown" in status strings (alongside "unhealthy")
3. Implement 3-way priority: unhealthy > unknown > healthy

This ensures consistency between:
- SSH-based updates (`GetContainersStatus`)
- Sentinel-based updates (`PushServerUpdateJob`)
- UI display logic

## Changes
- **app/Jobs/PushServerUpdateJob.php**: Added unknown status tracking
- **tests/Unit/PushServerUpdateJobStatusAggregationTest.php**: New comprehensive tests
- **tests/Unit/ExcludeFromHealthCheckTest.php**: Updated to match current implementation

## Testing
All 31 status-related unit tests passing:
- 18 tests in ContainerHealthStatusTest
- 8 tests in ExcludeFromHealthCheckTest (updated)
- 6 tests in PushServerUpdateJobStatusAggregationTest (new)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 13:40:58 +01:00
Andras Bacsai 08d257535a fix(docker): enhance container status aggregation for multi-container applications, including exclusion handling based on docker-compose configuration 2025-09-13 20:32:15 +02:00
Andras Bacsai 0f5c988658 fix(horizon): add silenced jobs 2025-07-12 14:44:32 +02:00
Andras Bacsai 24688b2ad8 fix(jobs): update middleware to use expireAfter for WithoutOverlapping in multiple job classes 2025-07-01 10:50:27 +02:00
Andras Bacsai f9a0ca2ca6 refactor(proxy): update StartProxy calls to use named parameter for async option 2025-06-16 13:13:01 +02:00
Andras Bacsai ddcb14500d refactor(proxy-status): refactored how the proxy status is handled on the UI and on the backend
feat(cloudflare): improved cloudflare tunnel automated installation
2025-06-06 14:47:54 +02:00
Andras Bacsai 97ec579910 refactor(push-server-update): enhance application preview handling by incorporating pull request IDs and adding status update protections 2025-06-04 10:03:36 +02:00
Andras Bacsai 9883cef26d refactor(jobs): update middleware to include job-specific identifiers for WithoutOverlapping 2025-05-29 17:31:43 +02:00
Andras Bacsai 0369909408 fix(PushServerUpdateJob): add null checks before updating application and database statuses 2025-05-29 10:47:26 +02:00
Andras Bacsai c6278a06ba refactor(jobs): unify middleware configuration to prevent job release after expiration for DockerCleanupJob and PushServerUpdateJob 2025-05-07 14:42:42 +02:00
Andras Bacsai b78f2cccff refactor(jobs): update WithoutOverlapping middleware to use expireAfter for better queue management 2025-04-18 09:52:32 +02:00
Andras Bacsai b09f0043d1 fix: restrict jobs on cloud
fix: restrict sentinel endpoint
2025-01-10 11:54:45 +01:00
Andras Bacsai 7dc65dfd79 fix: make sure important jobs/actions are running on high prio queue 2024-11-22 11:16:01 +01:00
Andras Bacsai 275edb6c1f put a few things on high queue 2024-11-06 12:33:56 +01:00
Lucas Michot 8e1444eaa7 Get rid of many useless blank lines 2024-10-31 17:44:01 +01:00
Andras Bacsai 96ca72fcdb refactor server view (phuuu) 2024-10-30 20:03:30 +01:00
Lucas Michot 5b6e466e0c Remove some useless catch blocks 2024-10-28 14:37:00 +01:00