Skip to content

feat(proxy): add gateway routes#9600

Open
ShadowArcanist wants to merge 8 commits into
coollabsio:nextfrom
ShadowArcanist:jean/proxy-gateway
Open

feat(proxy): add gateway routes#9600
ShadowArcanist wants to merge 8 commits into
coollabsio:nextfrom
ShadowArcanist:jean/proxy-gateway

Conversation

@ShadowArcanist
Copy link
Copy Markdown
Member

@ShadowArcanist ShadowArcanist commented Apr 16, 2026

Changes

  • Adds a UI to manage Traefik gateway routes in the server proxy

Issues

Notes:

  1. This is UI only because users who know to setup Traefik to be single entrypoint can do this by manually adding dynamic config, this UI is to simplify the process so new users can easily setup.

  2. Layer 7 proxying for now, this is enough for most users and if enough people ask for layer 4 then we can add it.

  3. The UI uses the dynamic config files as source of truth so if user edits the file manually then the UI will reflect it without any issues.

  4. This feature basically creates and edits dynamic config files so all files managed by this start with gateway- on the name and is readonly from the UI.

  5. The proxy will simply forward the request to the target, the target can be anything and is not tied to servers connected to Coolify, this allows users to set Tailscale clients or host:ip as target without any issues.

  6. This gateway feature is available on all servers connected to Coolify so user can make any of their server as a main entry point for flexibility.

  7. Each route is stored in its own separate dynamic configuration file, making it easier to edit, update, and delete compared to managing a single large file containing all routes.

Category

  • New feature

Preview

image image image image image

AI Assistance

  • AI was used (please describe below)

If AI was used:

  • Tools used: Jean + Claude (Opus 4.6)
  • How extensively: every change on this PR (except the PR description)

Testing

Used this feature to create the routes (which creates the dynamic config files) on local dev and used that dynamic conf on a staging server since I have DNS proper set on staging server and this feature only has to create dynamic config files correctly.

I tested editing, deleting, creating new file with same name (rejects new file and throws an error toast), and all are working fine.

Contributor Agreement

Important

  • I have read and understood the contributor guidelines. If I have failed to follow any guideline, I understand that this PR may be closed without review.
  • I have searched existing issues and pull requests (including closed ones) to ensure this isn't a duplicate.
  • I have tested all the changes thoroughly with a local development instance of Coolify and I am confident that they will work as expected when a maintainer tests them.

@ShadowArcanist ShadowArcanist added the 🛠️ Feature Issues requesting a new feature. label Apr 16, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Hi @ShadowArcanist! 👋

It appears to us that you are adding a new feature to Coolify.
We kindly ask you to also update the Coolify Documentation to include information about this new feature.
This will help ensure that our documentation remains accurate and up-to-date for all users.

Coolify Docs Repository: https://github.com/coollabsio/coolify-docs
How to Contribute to the Docs: https://coolify.io/docs/get-started/contribute/documentation

@Twest2
Copy link
Copy Markdown
Contributor

Twest2 commented Apr 16, 2026

Hey @ShadowArcanist this is much closer to the right direction than the closed team-wide auto-routing PR. It keeps routing per-route, preserves the remote server’s Traefik as the authority, and avoids mutating application labels. I think the remaining work is mostly around hardening the file-backed UI.

A few things I’d improve:

  1. Traefik-only behavior should be enforced in the UI.

    • The new Gateway page is shown for any server with a proxy in resources/views/components/server/sidebar-proxy.blade.php, but the implementation is Traefik-specific.
    • app/Livewire/Server/Proxy/GatewayRouteForm.php always generates Traefik http.routers/services/middlewares YAML, and app/Livewire/Server/Proxy/Gateway.php hardcodes gateway-*.yaml.
    • On a Caddy server this UI would let the user create routes that Caddy will ignore.
    • I’d either gate this feature to Traefik for now or add a real Caddy backend.
  2. The code assumes the filename is derived from the router name, which breaks the “dynamic files are the source of truth” goal.

    • Gateway::parseRoutes() discovers routes from router keys inside YAML, but readRouteConfig() / deleteRouteFile() reconstruct the file path as dynamic/<router_name>.yaml.
    • That falls apart if a user renames a file, keeps multiple routers in one file, or uses a router key that doesn’t match the filename.
    • I’d carry the actual source file path in the parsed route data and use that for edit/delete instead of regenerating the path from the router name.
  3. Rename flow is destructive if the write fails.

    • In app/Livewire/Server/Proxy/GatewayRouteForm.php, the old file is deleted before the new file is written.
    • If SSH or disk write fails between those steps, a working route is lost.
    • I’d write first and only delete after success, or do an atomic remote rename/move.
  4. The UI round-trip is lossy, but raw editing for gateway-*.yaml is disabled.

    • Gateway::parseRoutes() only understands a narrow subset of Traefik config.
    • GatewayRouteForm::buildRouteConfig() regenerates the whole file from that subset.
    • resources/views/livewire/server/proxy/dynamic-configurations.blade.php makes gateway-*.yaml read-only in the generic editor.
    • So if a route file contains extra middlewares, multiple upstream servers, headers, priority, transports, health checks, etc., opening and saving it through the gateway UI will silently strip those fields.
    • I’d either keep raw editing enabled for gateway files, or detect unsupported constructs and refuse UI-editing with a clear warning.
  5. TLS validation should match the form behavior.

    • The form says the cert resolver is required when TLS is enabled, but the backend does not enforce that.
    • Right now the UI can save tls: {} and present it as valid.
    • I’d add validation like required_if:tls_enabled,1 for the resolver, or explicitly support a “TLS enabled without resolver” mode if that is intentional.

Overall, I think the architecture is right. The biggest gap is making sure the file-backed model is actually robust when users manually edit files, because that’s the main reason this approach is safer than the old auto-derived team-wide routing design.

@Twest2
Copy link
Copy Markdown
Contributor

Twest2 commented Apr 17, 2026

I reviewed the latest state of this PR and found 3 remaining issues:

  1. High: Editing a route that omits entryPoints will silently pin it to https.
    app/Livewire/Server/Proxy/Gateway.php:332 reads a missing entryPoints as [], app/Livewire/Server/Proxy/GatewayRouteForm.php:55 turns that into an empty form field, and app/Livewire/Server/Proxy/GatewayRouteForm.php:70 defaults the save path back to ['https'].
    In Traefik, omitting entryPoints means "use the default entrypoints", so saving through this UI can narrow an existing route's exposure unexpectedly.
    Docs: https://doc.traefik.io/traefik/reference/install-configuration/entrypoints/

  2. High: Omitted passHostHeader is parsed as false, but Traefik defaults it to true.
    app/Livewire/Server/Proxy/Gateway.php:321 falls back to false, app/Livewire/Server/Proxy/Gateway.php:345 does not mark the field as missing, and app/Livewire/Server/Proxy/GatewayRouteForm.php:190 always writes that boolean back out.
    A valid file that relies on Traefik's default host-header forwarding will therefore be shown as No and rewritten to false on save, which can break downstream vhost routing.
    Docs: https://doc.traefik.io/traefik/reference/routing-configuration/http/load-balancing/service/

  3. Medium: The "extra config" guard still misses middleware options inside otherwise-supported middleware names.
    app/Livewire/Server/Proxy/Gateway.php:372 accepts *-stripprefix / *-redirect by name but never inspects their definitions, and app/Livewire/Server/Proxy/GatewayRouteForm.php:157 rewrites them from the reduced UI model.
    That means fields like stripPrefix.forceSlash: false are not warned about and will be dropped on save.
    Docs: https://doc.traefik.io/traefik/v3.4/middlewares/http/stripprefix/

@peaklabs-dev peaklabs-dev self-assigned this Apr 17, 2026
@Twest2
Copy link
Copy Markdown
Contributor

Twest2 commented Apr 18, 2026

Hey @ShadowArcanist, could you add support for UDP/TCP (level 4) packets aswell? This is something I worked a lot on in the other PR so I can implement it fairly easily if you'd like. This is absolutely something that I have to have in my setup.

@emmsdan
Copy link
Copy Markdown

emmsdan commented Apr 29, 2026

I am looking forward to this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🛠️ Feature Issues requesting a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants