Skip to content

Commit b806116

Browse files
feat: add subpath config (#1236)
Co-authored-by: Warren <[email protected]>
1 parent 6587283 commit b806116

File tree

9 files changed

+211
-2
lines changed

9 files changed

+211
-2
lines changed

.changeset/feat-subpath-config.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@hyperdx/app': minor
3+
---
4+
5+
feat: Add subpath configuration support
6+
7+
This change allows the HyperDX frontend to be served from a subpath (e.g.,
8+
`/hyperdx`). It includes updated Next.js, NGINX, and Traefik configurations,
9+
along with documentation for the new setup.

.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ HYPERDX_APP_PORT=8080
2020
HYPERDX_APP_URL=http://localhost
2121
HYPERDX_LOG_LEVEL=debug
2222
HYPERDX_OPAMP_PORT=4320
23+
HYPERDX_BASE_PATH=
2324

2425
# Otel/Clickhouse config
2526
HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE=default

docker-compose.dev.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,29 @@ services:
8383
interval: 1s
8484
timeout: 1s
8585
retries: 60
86+
87+
# nginx:
88+
# image: nginx:alpine
89+
# ports:
90+
# - '4040:4040'
91+
# volumes:
92+
# - ./proxy/nginx/nginx.conf.template:/etc/nginx/templates/default.conf.template:ro
93+
# environment:
94+
# HYPERDX_BASE_PATH: ${HYPERDX_BASE_PATH:-/}
95+
# network_mode: host
96+
# restart: always
97+
98+
# traefik:
99+
# image: traefik:latest
100+
# ports:
101+
# - '4040:4040'
102+
# volumes:
103+
# - ./proxy/traefik/traefik.yml:/etc/traefik/traefik.yml:ro
104+
# - ./proxy/traefik/config.yml:/etc/traefik/dynamic/config.yml:ro
105+
# environment:
106+
# HYPERDX_BASE_PATH: ${HYPERDX_BASE_PATH:-/}
107+
# network_mode: host
108+
# restart: always
109+
86110
networks:
87111
internal:

packages/app/.env.development

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
77
OTEL_SERVICE_NAME="hdx-oss-dev-app"
88
PORT=${HYPERDX_APP_PORT}
99
NODE_OPTIONS="--max-http-header-size=131072"
10+
NEXT_PUBLIC_HYPERDX_BASE_PATH=

packages/app/next.config.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ const withNextra = require('nextra')({
88
themeConfig: './src/nextra.config.tsx',
99
});
1010

11+
const basePath = process.env.NEXT_PUBLIC_HYPERDX_BASE_PATH;
12+
1113
module.exports = {
14+
basePath: basePath,
1215
experimental: {
1316
instrumentationHook: true,
1417
// External packages to prevent bundling issues with Next.js 14
@@ -57,8 +60,8 @@ module.exports = {
5760
productionBrowserSourceMaps: false,
5861
...(process.env.NEXT_OUTPUT_STANDALONE === 'true'
5962
? {
60-
output: 'standalone',
61-
}
63+
output: 'standalone',
64+
}
6265
: {}),
6366
}),
6467
};

proxy/README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Proxy Configuration
2+
3+
This directory contains configurations for running the HyperDX frontend behind a
4+
reverse proxy that serves the application under a specific subpath. This is
5+
useful for deployments where HyperDX is not at the root of a domain (e.g.,
6+
`http://example.com/hyperdx`).
7+
8+
We provide configurations for two popular reverse proxies:
9+
10+
- [Nginx](./nginx/nginx.conf.template)
11+
- [Traefik](./traefik/config.yml)
12+
13+
## Environment Variables
14+
15+
To configure the subpath, you need to set the following environment variables in
16+
your `.env` file.
17+
18+
### `HYPERDX_BASE_PATH` and `NEXT_PUBLIC_HYPERDX_BASE_PATH`
19+
20+
To serve the application from a subpath, two environment variables must be set
21+
to the **same value**:
22+
23+
1. `HYPERDX_BASE_PATH`: This is used by the reverse proxy (Nginx or Traefik) to
24+
handle path routing and rewriting.
25+
2. `NEXT_PUBLIC_HYPERDX_BASE_PATH`: This is used by the Next.js application to
26+
generate correct asset links and API routes.
27+
28+
- The value **must** start with a `/` if it's not an empty string (ex:
29+
`/hyperdx`).
30+
- If you want to serve from the root, you can omit these variables or set them
31+
to `/`.
32+
33+
### `FRONTEND_URL`
34+
35+
This variable should be set to the full public URL of the frontend, including
36+
the subpath. The API server uses this URL for various purposes such as
37+
generating absolute URLs for redirects, links in emails, or alerts.
38+
39+
- It should be a full URL, including the protocol (`http` or `https`).
40+
- It should include the subpath defined in `HYPERDX_BASE_PATH`.
41+
42+
**Example `.env` Configuration:**
43+
44+
For local development with the subpath `/hyperdx`, your configuration would look
45+
like this:
46+
47+
```
48+
HYPERDX_BASE_PATH=/hyperdx
49+
NEXT_PUBLIC_HYPERDX_BASE_PATH=/hyperdx
50+
FRONTEND_URL=http://localhost:4040/hyperdx
51+
```
52+
53+
## How It Works
54+
55+
The proxy configurations are designed to handle subpath routing with minimal
56+
changes to the application code. Here's a high-level overview of the logic:
57+
58+
1. **Root Redirect**: If a subpath is configured (e.g., `/hyperdx`), any
59+
requests to the root (`/`) are automatically redirected to that subpath.
60+
This ensures users always land on the correct URL.
61+
62+
2. **Path Rewriting**: The application's frontend code sometimes makes requests
63+
to root-level paths (e.g., `/api/...` or `/_next/...`). The proxy intercepts
64+
these requests, prepends the configured subpath, and forwards them to the
65+
Next.js server. For example, a request for `/_next/static/chunk.js` becomes
66+
a request for `/hyperdx/_next/static/chunk.js` before being sent to the
67+
application.
68+
69+
3. **Direct Proxy**: Any requests that already include the correct subpath are
70+
passed directly to the Next.js application, which is configured via
71+
`basePath` to handle them correctly.
72+
73+
This setup allows the frontend application to be developed as if it were running
74+
at the root, while the proxy transparently manages the subpath routing.

proxy/nginx/nginx.conf.template

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
upstream app {
2+
server 127.0.0.1:8080;
3+
}
4+
5+
server {
6+
listen 4040;
7+
8+
set $base_path "${HYPERDX_BASE_PATH}";
9+
if ($base_path = "/") {
10+
set $base_path "";
11+
}
12+
13+
# Common proxy headers
14+
proxy_http_version 1.1;
15+
proxy_set_header Upgrade $http_upgrade;
16+
proxy_set_header Connection 'upgrade';
17+
proxy_set_header Host $host;
18+
proxy_cache_bypass $http_upgrade;
19+
proxy_set_header X-Real-IP $remote_addr;
20+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
21+
proxy_set_header X-Forwarded-Proto $scheme;
22+
23+
# Redirect root to base path, if a base path is set
24+
location = / {
25+
if ($base_path != "") {
26+
return 301 $base_path;
27+
}
28+
# If no base path, just proxy to the app
29+
proxy_pass http://app;
30+
}
31+
32+
# This handles assets and api calls made to the root and rewrites them to include the base path
33+
location ~ ^(/api/|/_next/|/__ENV\.js$|/Icon32\.png$) {
34+
# Note: $request_uri includes the original full path including query string
35+
proxy_pass http://app$base_path$request_uri;
36+
}
37+
38+
# Proxy requests that are already prefixed with the base path to the app
39+
location ${HYPERDX_BASE_PATH} {
40+
# The full request URI (e.g., /hyperdx/settings) is passed to the upstream
41+
proxy_pass http://app;
42+
}
43+
}

proxy/traefik/config.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
http:
2+
routers:
3+
# This handles the main app at the basepath
4+
app-router:
5+
entryPoints:
6+
- web
7+
rule: 'PathPrefix(`{{ env "HYPERDX_BASE_PATH" }}`)'
8+
service: app-service
9+
10+
# This handles assets and api calls at the root and rewrites them
11+
assets-api-router:
12+
entryPoints:
13+
- web
14+
rule:
15+
'PathPrefix(`/api`) || PathPrefix(`/_next`) || Path(`/__ENV.js`) ||
16+
Path(`/Icon32.png`)'
17+
service: app-service
18+
middlewares:
19+
- add-basepath
20+
21+
# This redirects from / to the basepath
22+
root-redirect:
23+
entryPoints:
24+
- web
25+
rule: 'Path(`/`)'
26+
service: app-service # service is required, but redirect will happen first
27+
middlewares:
28+
- redirect-to-basepath
29+
30+
middlewares:
31+
add-basepath:
32+
addPrefix:
33+
prefix: '{{ env "HYPERDX_BASE_PATH" }}'
34+
35+
redirect-to-basepath:
36+
redirectRegex:
37+
regex: '^/$'
38+
replacement: '{{ env "HYPERDX_BASE_PATH" }}'
39+
permanent: true
40+
41+
services:
42+
app-service:
43+
loadBalancer:
44+
passHostHeader: true
45+
servers:
46+
- url: 'http://127.0.0.1:8080'

proxy/traefik/traefik.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
entryPoints:
2+
web:
3+
address: ':4040'
4+
5+
providers:
6+
file:
7+
filename: /etc/traefik/dynamic/config.yml
8+
watch: true

0 commit comments

Comments
 (0)