Migrate realtime services to Reverb

New Coolify releases run Laravel Reverb and the web terminal server inside the main coolify container. They no longer use the separate coolify-realtime container.

Most installations do not need a manual change. Review your configuration before the update if you use a custom Compose override, custom realtime ports, a firewall, or a Cloudflare Tunnel.

Legacy overrides remain compatible

The update accepts a soketi override in docker-compose.custom.yml. Existing proxy routes and port settings remain compatible. You can move custom port bindings to coolify to make the override reflect the new container layout.

What changes

BeforeAfter
Soketi listens in coolify-realtimeReverb listens in coolify
The terminal server listens in coolify-realtimeThe terminal server listens in coolify
The proxy sends realtime traffic to coolify-realtime:6001The proxy sends realtime traffic to coolify:6001
The proxy sends terminal traffic to coolify-realtime:6002The proxy sends terminal traffic to coolify:6002

The external paths do not change:

  • /app/* carries browser WebSocket connections.
  • /apps/* carries Pusher-compatible API requests.
  • /terminal/ws carries web terminal connections.

The update keeps the existing Pusher application ID, key, and secret. It also keeps SOKETI_PORT as the compatible Docker host-port setting. Do not rename that variable during this migration.

The update script also removes the old coolify-realtime container. If .env still contains PUSHER_BACKEND_HOST=coolify-realtime, the script changes it to 127.0.0.1, because Reverb now runs in the same container as Coolify.

The coolify container also has coolify-realtime as a network alias on the coolify Docker network. A reverse proxy that still sends traffic to coolify-realtime:6001 or coolify-realtime:6002 keeps working.

Before you update

Back up the instance configuration

cd /data/coolify/source
cp .env .env.before-reverb

if [ -f docker-compose.custom.yml ]; then
  cp docker-compose.custom.yml docker-compose.custom.yml.before-reverb
fi

Check for a Soketi service override

grep -nE '^[[:space:]]*soketi:' docker-compose.custom.yml 2>/dev/null || true

If the command prints a line, review the override. Migrating it as shown below is optional; legacy soketi overrides are still accepted.

Record custom realtime settings

grep -E '^(PUSHER_HOST|PUSHER_PORT|PUSHER_SCHEME|PUSHER_BACKEND_PORT|SOKETI_PORT|TERMINAL_PORT)=' .env || true

Keep this output until you finish the verification.

Migrate a custom Compose override

The old firewall example configured ports on two services:

services:
  coolify:
    ports: !override
      - "127.0.0.1:${APP_PORT:-8000}:8080"
  soketi:
    ports: !override
      - "127.0.0.1:${SOKETI_PORT:-6001}:6001"
      - "127.0.0.1:6002:6002"

Move all three bindings to the coolify service:

services:
  coolify:
    ports: !override
      - "127.0.0.1:${APP_PORT:-8000}:8080"
      - "127.0.0.1:${SOKETI_PORT:-6001}:6001"
      - "127.0.0.1:6002:6002"

The web terminal always uses host port 6002. TERMINAL_PORT does not change the Docker host port.

If your override removed all public bindings, replace this:

services:
  coolify:
    ports: !override []
  soketi:
    ports: !override []

with this:

services:
  coolify:
    ports: !override []

Move only port bindings that are still required. Remove Soketi-specific image, command, environment, health-check, and volume settings. Remove the soketi service block when it is empty.

Do not remove direct access too early

Keep ports 8000, 6001, and 6002 reachable until the dashboard, realtime updates, and terminal work through your domain. You can restrict them after you complete the verification.

Check the environment variables

The variables have different purposes:

VariablePurpose
SOKETI_PORTCompatible Docker host-port override for origin port 6001
REVERB_PORTOptional. Takes precedence over SOKETI_PORT for the Docker host port of origin port 6001. You do not need it when you already use SOKETI_PORT.
PUSHER_PORTPublic port used by the browser
PUSHER_BACKEND_PORTInternal Reverb listen port, normally 6001
PUSHER_BACKEND_HOSTHost that Coolify uses to send events to Reverb, normally unset or 127.0.0.1
TERMINAL_PORTPublic port used by the browser for the web terminal, for example 443 behind a reverse proxy. It does not change the Docker host port, which is always 6002.

Do not change an existing SOKETI_PORT value. Existing firewall rules and port forwarding can depend on it.

For a dashboard behind Cloudflare, the public browser port is normally 443:

PUSHER_PORT=443
PUSHER_BACKEND_PORT=6001

Do not set PUSHER_PORT=6001 for a Cloudflare-proxied hostname. Port 6001 is the origin port. The browser connects to Cloudflare on port 443.

If you do not have an explicit PUSHER_PORT, leave it unset. Coolify selects the public port from the dashboard request and keeps direct IP access compatible.

Cloudflare configurations

Dashboard through the Coolify proxy

This is the recommended configuration. Send the dashboard hostname to the Coolify proxy on port 80 or 443. The generated proxy configuration routes:

/app/*       -> coolify:6001
/apps/*      -> coolify:6001
/terminal/ws -> coolify:6002
/*           -> coolify:8080

You do not need separate Cloudflare routes for Reverb or the terminal.

For a Cloudflare Tunnel that terminates TLS at Cloudflare, set the tunnel service URL to http://localhost:80. For end-to-end TLS, use the HTTPS origin configuration from the Full TLS guide.

Separate realtime hostname

Older tunnel configurations can use a separate hostname:

Public hostname or pathTunnel service
coolify.example.comhttp://localhost:8000
coolify.example.com/terminal/wshttp://localhost:6002
coolify-realtime.example.comhttp://localhost:6001

This remains compatible. Keep:

PUSHER_HOST=coolify-realtime.example.com
PUSHER_PORT=443
PUSHER_BACKEND_PORT=6001

The Cloudflare route still connects to origin port 6001, but Reverb now handles that port in the coolify container.

Update

Update Coolify from Settings > Updates. Existing realtime routes, port settings, and legacy soketi overrides remain compatible. Keep the configuration backups until you have verified the dashboard, realtime updates, and terminal.

If you choose to migrate an override manually, check that all required port bindings are now on coolify. Review the update log in /data/coolify/source/upgrade-*.log if the update reports a configuration error.

Verify the migration

Run these checks after the update:

docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker logs --tail 100 coolify

Confirm that:

  1. coolify, coolify-db, and coolify-redis are running.
  2. The coolify-realtime container is gone. The update removes it.
  3. The dashboard loads through its normal URL.
  4. The browser developer tools show a successful WebSocket request to /app/....
  5. The WebSocket request uses wss and public port 443 when Cloudflare serves the dashboard over HTTPS.
  6. A deployment or status change appears in the dashboard without a page refresh.
  7. The web terminal connects through /terminal/ws.

You can also open /realtime in two browser tabs and send a test event.

Troubleshooting

The coolify container stays unhealthy

The container healthcheck now includes Reverb. Check the Reverb log inside the container and confirm that nothing else listens on port 6001:

docker exec coolify curl --fail http://127.0.0.1:6001/up
sudo ss -tulpn | grep :6001

The dashboard keeps working while the container is unhealthy. Only realtime updates stop.

Cloudflare returns an error for the WebSocket request

  • Confirm the browser connects to public port 443, not origin port 6001.
  • Confirm Cloudflare WebSockets are enabled for the zone.
  • Confirm the tunnel or proxy route includes /app/*.
  • Confirm the Coolify proxy can reach coolify:6001 on the coolify Docker network.

The dashboard works but realtime updates do not

Check PUSHER_HOST and PUSHER_PORT in /data/coolify/source/.env. Remove obsolete overrides when the dashboard and Reverb use the same hostname, or keep port 443 for a separate Cloudflare realtime hostname.

Also confirm that PUSHER_BACKEND_HOST is unset or 127.0.0.1. A public hostname in that variable sends server-side events to the wrong place.

Direct IP access stops receiving updates

Confirm that the coolify container publishes host port ${REVERB_PORT:-${SOKETI_PORT:-6001}} to container port 6001 and host port 6002 to container port 6002. Also confirm that the server firewall permits these host ports from your client address.

On this page