新增docker支持

This commit is contained in:
imengying committed 2026-09-14 23:09:02 +08:00
1 parent 0ef99ec44d
commit 37d8c663c7
8 files changed
+437 -219

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
**
!Dockerfile
!LICENSE
!package.json
!bun.lock
!frontend/
!frontend/**
!backend/
!backend/Cargo.toml
!backend/Cargo.lock
!backend/src/
!backend/src/**
!backend/migrations/
!backend/migrations/**
!shared/
!shared/Cargo.toml
!shared/Cargo.lock
!shared/*.rs
**/node_modules
**/dist
**/admin-dist
**/target
**/.env*
**/*.tsbuildinfo
**/config.toml
**/*.db
**/*.db-shm
**/*.db-wal
+181
View File
@@ -0,0 +1,181 @@
name: Publish Docker Hub image
on:
push:
tags:
- "v*.*.*"
permissions:
contents: read
env:
IMAGE_NAME: gxmandppx/nodeflare
concurrency:
group: docker-hub-${{ github.ref }}
cancel-in-progress: false
jobs:
metadata:
name: Resolve image tags
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
version: ${{ steps.version.outputs.version }}
tags: ${{ steps.version.outputs.tags }}
rust_image: ${{ steps.base_images.outputs.rust_image }}
alpine_image: ${{ steps.base_images.outputs.alpine_image }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Resolve version and tags
id: version
shell: bash
env:
NODEFLARE_VERSION: ${{ github.ref_name }}
run: |
set -euo pipefail
version=$(sh scripts/resolve-version.sh)
printf 'version=%s\ntags=%s,latest,sha-%.12s\n' "$version" "$version" "$GITHUB_SHA" >> "$GITHUB_OUTPUT"
- name: Log in to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
username: ${{ vars.DOCKER_USERNAME || secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
- name: Resolve latest stable base images
id: base_images
shell: bash
run: |
set -euo pipefail
# Resolve once so both architectures and the test/push builds use the same images.
for item in rust_image=rust:1-alpine alpine_image=alpine:latest; do
key=${item%%=*}
image=${item#*=}
digest=$(docker buildx imagetools inspect "$image" --format '{{json .Manifest}}' | jq -er '.digest')
if [[ ! "$digest" =~ ^sha256:[a-f0-9]{64}$ ]]; then
echo "Invalid base image digest for $image" >&2
exit 1
fi
printf '%s=%s@%s\n' "$key" "$image" "$digest" >> "$GITHUB_OUTPUT"
done
build:
name: Build and verify (${{ matrix.arch }})
needs: metadata
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-latest
- arch: arm64
runner: ubuntu-24.04-arm
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Log in to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
username: ${{ vars.DOCKER_USERNAME || secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
- name: Build image for smoke test
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
platforms: linux/${{ matrix.arch }}
build-args: |
NODEFLARE_VERSION=${{ needs.metadata.outputs.version }}
RUST_IMAGE=${{ needs.metadata.outputs.rust_image }}
ALPINE_IMAGE=${{ needs.metadata.outputs.alpine_image }}
tags: nodeflare:smoke
load: true
provenance: false
cache-from: type=gha,scope=nodeflare-${{ matrix.arch }}
- name: Verify version, startup and frontend assets
env:
NODEFLARE_VERSION: ${{ needs.metadata.outputs.version }}
run: sh scripts/smoke-test-docker.sh nodeflare:smoke
- name: Push verified image by digest
id: push
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
platforms: linux/${{ matrix.arch }}
build-args: |
NODEFLARE_VERSION=${{ needs.metadata.outputs.version }}
RUST_IMAGE=${{ needs.metadata.outputs.rust_image }}
ALPINE_IMAGE=${{ needs.metadata.outputs.alpine_image }}
labels: |
org.opencontainers.image.title=NodeFlare
org.opencontainers.image.description=NodeFlare server with public and admin frontends
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.version=${{ needs.metadata.outputs.version }}
org.opencontainers.image.licenses=MIT
outputs: type=image,name=${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
provenance: true
sbom: true
cache-from: type=gha,scope=nodeflare-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=nodeflare-${{ matrix.arch }}
- name: Export image digest
env:
IMAGE_DIGEST: ${{ steps.push.outputs.digest }}
run: |
mkdir -p "$RUNNER_TEMP/nodeflare-digests"
touch "$RUNNER_TEMP/nodeflare-digests/${IMAGE_DIGEST#sha256:}"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: docker-digest-${{ matrix.arch }}
path: ${{ runner.temp }}/nodeflare-digests/*
if-no-files-found: error
retention-days: 1
publish:
name: Publish multi-platform tags
needs: [metadata, build]
runs-on: ubuntu-latest
timeout-minutes: 10
env:
IMAGE_TAGS: ${{ needs.metadata.outputs.tags }}
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: docker-digest-*
path: ${{ runner.temp }}/nodeflare-digests
merge-multiple: true
- name: Log in to Docker Hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
username: ${{ vars.DOCKER_USERNAME || secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
- name: Create and inspect image manifest
shell: bash
run: |
set -euo pipefail
IFS=, read -r -a tags <<< "$IMAGE_TAGS"
tag_args=()
for tag in "${tags[@]}"; do
tag_args+=(--tag "$IMAGE_NAME:$tag")
done
digest_args=()
for file in "$RUNNER_TEMP"/nodeflare-digests/*; do
digest=${file##*/}
if [[ ! "$digest" =~ ^[a-f0-9]{64}$ ]]; then
echo "Invalid image digest artifact" >&2
exit 1
fi
digest_args+=("$IMAGE_NAME@sha256:$digest")
done
if [ "${#digest_args[@]}" -ne 2 ]; then
echo "Both amd64 and arm64 images are required before publishing tags" >&2
exit 1
fi
docker buildx imagetools create "${tag_args[@]}" "${digest_args[@]}"
docker buildx imagetools inspect "$IMAGE_NAME:${tags[0]}"
+3
View File
@@ -9,6 +9,9 @@
/frontend/admin-dist/
/release/
# Docker 持久化数据
/data/
# 依赖
/node_modules/
/frontend/node_modules/
+52
View File
@@ -0,0 +1,52 @@
# syntax=docker/dockerfile:1
ARG RUST_IMAGE=rust:1-alpine
ARG ALPINE_IMAGE=alpine:latest
FROM oven/bun:1.4.0@sha256:5ff609364c049b54eb0ff560ec96319729a972078ef2c755d758f0c6ef89c2d6 AS frontend
WORKDIR /build
COPY package.json bun.lock ./
COPY frontend/package.json ./frontend/package.json
RUN bun install --frozen-lockfile
COPY frontend/ ./frontend/
ARG NODEFLARE_VERSION=1.0.0
ENV VITE_NODEFLARE_VERSION=${NODEFLARE_VERSION}
RUN bun run --cwd frontend build
FROM ${RUST_IMAGE} AS backend
SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
# Package versions follow security updates for the selected Alpine branch.
# hadolint ignore=DL3018
RUN apk add --no-cache build-base
WORKDIR /build
COPY backend/ ./backend/
COPY shared/ ./shared/
ARG NODEFLARE_VERSION=1.0.0
ARG TARGETARCH
ENV NODEFLARE_VERSION=${NODEFLARE_VERSION}
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,id=nodeflare-musl-target-${TARGETARCH},target=/build/backend/target,sharing=locked \
rust_target="$(rustc -vV | sed -n 's/^host: //p')" \
&& RUSTFLAGS="-C target-feature=+crt-static" cargo build --manifest-path backend/Cargo.toml --release --locked --target "$rust_target" \
&& cp "backend/target/$rust_target/release/nodeflare" /usr/local/bin/nodeflare \
&& readelf -l /usr/local/bin/nodeflare > /tmp/nodeflare-program-headers \
&& ! grep -q INTERP /tmp/nodeflare-program-headers
FROM ${ALPINE_IMAGE}
# hadolint ignore=DL3018
RUN apk add --no-cache ca-certificates \
&& addgroup -S -g 10001 nodeflare \
&& adduser -S -D -H -u 10001 -G nodeflare -s /sbin/nologin nodeflare \
&& mkdir -p /etc/nodeflare \
&& chown nodeflare:nodeflare /etc/nodeflare
COPY --from=backend /usr/local/bin/nodeflare /usr/local/bin/nodeflare
COPY --from=frontend /build/frontend/dist/ /opt/nodeflare/share/frontend/
COPY --from=frontend /build/frontend/admin-dist/ /opt/nodeflare/share/admin/
COPY LICENSE /usr/share/doc/nodeflare/LICENSE
WORKDIR /etc/nodeflare
USER 10001:10001
VOLUME ["/etc/nodeflare"]
EXPOSE 2206
ENTRYPOINT ["/usr/local/bin/nodeflare"]
CMD ["--config", "/etc/nodeflare/config.toml"]
+33 -1
View File
@@ -11,7 +11,7 @@ NodeFlare 是一款轻量级、可自托管的服务器监控面板:通过 Web
> [!WARNING]
> NodeFlare 是一款自托管的监控/控制程序,仅应部署在你拥有或已获得授权管理的系统上。在未获授权的系统上部署、访问或执行命令,由部署者自行承担责任。
[在线文档](https://elysia62.github.io/NodeFlareWiki/) | [常见问题](https://elysia62.github.io/NodeFlareWiki/faq.html) | [发布下载](https://github.com/elysia62/NodeFlare/releases)
[在线文档](https://elysia62.github.io/NodeFlareWiki/) | [在线演示](https://elysia62.github.io/NodeFlareWiki/demo/) | [常见问题](https://elysia62.github.io/NodeFlareWiki/faq.html) | [发布下载](https://github.com/elysia62/NodeFlare/releases)
## 界面预览
@@ -66,6 +66,38 @@ curl -fsSL https://raw.githubusercontent.com/elysia62/NodeFlare/main/agent/agent
> [!TIP]
> 安装脚本参数、各平台 Agent 脚本与卸载方法见文档:[安装服务端](https://elysia62.github.io/NodeFlareWiki/guide/quick-start.html) · [安装 Agent](https://elysia62.github.io/NodeFlareWiki/guide/agent.html) · [卸载](https://elysia62.github.io/NodeFlareWiki/guide/uninstall.html)。
## Docker 部署
镜像:[`gxmandppx/nodeflare`](https://hub.docker.com/r/gxmandppx/nodeflare),支持 amd64 / arm64。两种方式均需先按[配置示例](docker/config.example.toml)准备 `./data/config.toml`,并确保 `./data` 及配置文件可由容器用户 `10001:10001` 读写。
Docker:
```bash
docker run -d --name nodeflare \
--restart unless-stopped \
-p 127.0.0.1:2206:2206 \
-v "$PWD/data:/etc/nodeflare" \
gxmandppx/nodeflare:latest
```
Docker Compose(将以下内容保存为 `compose.yaml`):
```yaml
services:
nodeflare:
image: gxmandppx/nodeflare:latest
container_name: nodeflare
restart: unless-stopped
ports:
- "127.0.0.1:2206:2206"
volumes:
- ./data:/etc/nodeflare
```
```bash
docker compose up -d
```
## 文档
| 文档 | 说明 |
+15
View File
@@ -0,0 +1,15 @@
# Copy to data/config.toml and set the initial administrator credentials.
database_url = "sqlite://nodeflare.db"
bind_addr = "0.0.0.0:2206"
admin_username = "admin"
# Used only for the first database initialization; cleared after startup.
admin_password = "CHANGE_ME_WITH_A_STRONG_PASSWORD"
frontend_dir = "/opt/nodeflare/share/frontend"
admin_frontend_dir = "/opt/nodeflare/share/admin"
theme_dir = "themes"
trusted_proxies = ["127.0.0.1/32", "::1/128"]
turnstile_site_key = ""
turnstile_secret_key = ""
session_ttl_hours = 168
+69 -218
View File
@@ -1,21 +1,17 @@
# NodeFlare
![Release](https://img.shields.io/github/v/release/elysia62/NodeFlare)
![License](https://img.shields.io/github/license/elysia62/NodeFlare)
![Platform](https://img.shields.io/badge/platform-Linux%20%7C%20Windows%20%7C%20macOS%20%7C%20FreeBSD-blue)
English | [简体中文](../README.md)
NodeFlare is a lightweight, self-hosted server monitoring panel: view server status in a web UI while a small agent collects and pushes telemetry. It supports live metrics, TCP/ICMP probing, Telegram alerts, remote command execution, TOTP two-factor authentication, theme customization, and SQLite / PostgreSQL backup, restore, and online migration.
Chinese: [README.md](../README.md)
> [!WARNING]
> NodeFlare is a self-hosted monitoring and control program and should only be deployed on systems you own or are authorized to manage. Deploying, accessing, or executing commands on unauthorized systems is at the deployer's own responsibility.
- [Screenshots](#screenshots)
- [Features](#features)
- [Supported platforms](#supported-platforms)
- [Install the server](#install-the-server)
- [Install the agent](#install-the-agent)
- [Metrics and sampling](#metrics-and-sampling)
- [Configuration](#configuration)
- [Default paths and logs](#default-paths-and-logs)
- [Reverse proxy](#reverse-proxy)
- [Database and backups](#database-and-backups)
- [Development](#development)
- [Troubleshooting](#troubleshooting)
[Documentation](https://elysia62.github.io/NodeFlareWiki/en/) | [Live Demo](https://elysia62.github.io/NodeFlareWiki/demo/) | [FAQ](https://elysia62.github.io/NodeFlareWiki/en/faq.html) | [Releases](https://github.com/elysia62/NodeFlare/releases)
## Screenshots
@@ -25,50 +21,16 @@ Chinese: [README.md](../README.md)
## Features
**Monitoring**
- **Live metrics**: CPU, memory, and network speed sampled every second and uploaded in compressed batches every 3 seconds by default; slower metrics such as disks, GPU, and connection counts cached separately
- **Latency probing**: TCP and ICMP tasks assigned per node, with per-carrier (China Telecom / Mobile / Unicom) display
- **Alerts**: resource thresholds, offline, expiry, and traffic alerts pushed through Telegram with customizable message templates
- **Theme store**: built-in themes, one-click install from GitHub repositories, local ZIP upload, and per-theme settings
- **Secure by default**: TOTP two-factor authentication, Cloudflare Turnstile, login rate limiting, and session management; the server listens on `127.0.0.1` only, and the agent connects outbound with no inbound ports
- **Data ownership**: SQLite and PostgreSQL with online migration between them, one-click backup and restore, and automatic history cleanup by retention days
- CPU, memory, and network speed are sampled every second and uploaded in compressed batches every 3 seconds by default; slower metrics such as disks, GPU, and connection counts are cached separately
- Latency probing: TCP and ICMP tasks assigned per node, with per-carrier (China Telecom / Mobile / Unicom) display
- Public dashboard in Simplified Chinese and English; site name, announcement, logo, background image, and visible sections are configurable
- Theme store: built-in themes, one-click install from the repository, local ZIP upload, and per-theme settings
## Quick Start
**Alerts**
- Resource threshold rules for CPU / memory / disk / inbound / outbound, using averages or continuous breach with a configurable duration
- Offline, expiry, and traffic alerts delivered through Telegram with a customizable message template
**Management**
- Node grouping, tags, region, billing cycle, price, expiry date, traffic quota, and reset day
- Remote command execution: requires TOTP; a command runs for up to 10 minutes and is not cancelled when you leave the page
- Daily exchange-rate snapshot for multi-currency pricing
**Security**
- TOTP two-factor authentication; Cloudflare Turnstile (can protect admin login and the public dashboard independently)
- Login rate limiting, session management (list login devices and revoke them), configurable session lifetime
- Listens on `127.0.0.1` by default; the agent connects outbound, so no inbound port is required; the agent token can be passed as a file or an environment variable
**Data**
- SQLite and PostgreSQL, with online migration between them
- One-click ZIP backup / restore, space reclamation, and automatic cleanup of history by retention days
## Supported platforms
| Role | Platform | Architecture |
| --- | --- | --- |
| Server | Linux | x64 / ARM64 |
| Server | Windows | x64 |
| Server | macOS | ARM64 |
| Server | FreeBSD 13+ | x64 / ARM64 |
| Agent | Linux / Windows / macOS / FreeBSD | Same as above (macOS is Apple Silicon only) |
The installer detects the service manager (systemd / OpenRC / launchd / FreeBSD rc / Windows scheduled task) and registers the panel to start on boot.
## Install the server
The installer downloads the latest release and registers the system service.
Install the server (downloads the latest release and registers a system service automatically).
Linux / macOS:
@@ -82,7 +44,7 @@ FreeBSD:
fetch -qo - https://raw.githubusercontent.com/elysia62/NodeFlare/main/install.sh | sudo sh
```
Windows PowerShell (administrator):
Windows PowerShell (Administrator):
```powershell
Invoke-WebRequest -UseBasicParsing https://raw.githubusercontent.com/elysia62/NodeFlare/main/install.ps1 -OutFile "$env:TEMP\nodeflare-install.ps1"
@@ -90,144 +52,67 @@ Unblock-File "$env:TEMP\nodeflare-install.ps1"
& "$env:TEMP\nodeflare-install.ps1"
```
The first install asks for the admin username, password, listening port (default 2206), and database URL (SQLite by default). The panel listens on `127.0.0.1:2206`, so open `http://127.0.0.1:2206/admin/login` locally; public access requires an HTTPS reverse proxy, see [Reverse proxy](#reverse-proxy).
The first install asks for the admin username, password, listen port (default 2206), and database URL (SQLite by default). The server listens on `127.0.0.1:2206` by default — access it locally at `http://127.0.0.1:2206/admin/login`; use an HTTPS reverse proxy for external access.
The admin password is only needed to initialize the database for the first time; it is cleared from the configuration afterwards.
### Installer options
| Command | Description |
| --- | --- |
| `sudo sh install.sh` | Interactive menu |
| `sudo sh install.sh --install` | Install or update |
| `sudo sh install.sh --status` | Show service status |
| `sudo sh install.sh --restart` | Restart the service |
| `sudo sh install.sh --uninstall` | Uninstall, keeping configuration and data |
| `sudo sh install.sh --uninstall --purge` | Uninstall and delete configuration and data |
On Windows the equivalent options are `-Install` / `-Status` / `-Restart` / `-Uninstall [-Purge]`.
To update, run the installer again; configuration and data are preserved. Installs and updates verify the release digest and roll back to the previous version on failure.
## Install the agent
Create a node on the "Servers" page in the admin panel and run the command shown in the dialog. The agent connects outbound to the panel, so no inbound port is required.
Linux:
Create a node on the **Servers** page of the admin panel and run the install command shown in its dialog to install the agent:
```bash
curl -fsSL https://raw.githubusercontent.com/elysia62/NodeFlare/main/agent/agent.sh \
| sudo sh -s -- -e 'https://nodeflare.example.com' -t 'Agent Token'
```
Per-platform scripts:
Updating: re-run the install script. Config and data are preserved and a failed update rolls back automatically.
| Platform | Script | Service |
| --- | --- | --- |
| Linux | `agent/agent.sh` | systemd / OpenRC |
| macOS (Apple Silicon) | `agent/install-macos.sh` | launchd |
| FreeBSD | `agent/install-freebsd.sh` | rc.d |
| Windows | `agent/install.ps1` | Scheduled task |
> [!TIP]
> Script options, per-platform agent scripts, and uninstall steps are documented in [Install the Server](https://elysia62.github.io/NodeFlareWiki/en/guide/quick-start.html) · [Install the Agent](https://elysia62.github.io/NodeFlareWiki/en/guide/agent.html) · [Uninstall](https://elysia62.github.io/NodeFlareWiki/en/guide/uninstall.html).
### Agent options
## Docker Deployment
| Option | Description |
Image: [`gxmandppx/nodeflare`](https://hub.docker.com/r/gxmandppx/nodeflare), available for amd64 / arm64. Both options require `./data/config.toml` based on the [example configuration](../docker/config.example.toml), with `./data` and the configuration file writable by container user `10001:10001`.
Docker:
```bash
docker run -d --name nodeflare \
--restart unless-stopped \
-p 127.0.0.1:2206:2206 \
-v "$PWD/data:/etc/nodeflare" \
gxmandppx/nodeflare:latest
```
Docker Compose (save the following as `compose.yaml`):
```yaml
services:
nodeflare:
image: gxmandppx/nodeflare:latest
container_name: nodeflare
restart: unless-stopped
ports:
- "127.0.0.1:2206:2206"
volumes:
- ./data:/etc/nodeflare
```
```bash
docker compose up -d
```
## Documentation
| Document | Description |
| --- | --- |
| `-e` | NodeFlare endpoint (required) |
| `-t` | Agent token (required) |
| `-i` | Initial history interval in seconds, 15–3600 (default 60) |
| `-m` | Optional GitHub download mirror prefix, e.g. `https://ghproxy.net` |
| `--update` | Update the agent, reusing the saved endpoint and token, verifying the digest and rolling back on failure |
| `--status` | Show agent status |
| `--uninstall` | Uninstall the agent |
On Windows the equivalents are `-Endpoint` / `-Token` / `-Interval` / `-Mirror` and `-Update` / `-Status` / `-Uninstall`.
On Linux with systemd, the installer writes the token directly into the service unit as `Environment=NODEFLARE_AGENT_TOKEN=...`. The `--update` command reads the endpoint, token, and history interval from that service configuration.
## Metrics and sampling
CPU, memory, and network speed are sampled every second and uploaded in compressed batches every 3 seconds by default. Server cards show one real sample per second, giving roughly 2–3 seconds of display latency on a healthy connection; after a reconnect, backlogged samples are skipped. Slower metrics such as disk capacity and GPU are cached separately, and history is aggregated by each node's configured interval. A longer real-time upload interval reduces how often cards update.
Linux memory accounting follows the Komari convention: used memory is `MemTotal - MemFree - Cached - SReclaimable - Buffers + Shmem`, and used swap subtracts `SwapCached`. File cache is not counted as used memory and shared memory is; the panel reports whole-machine memory, not NodeFlare's own usage.
## Configuration
The configuration file location is listed in [Default paths and logs](#default-paths-and-logs); a full example is in [`backend/config.example.toml`](../backend/config.example.toml).
| Key | Description |
| --- | --- |
| `database_url` | `sqlite://nodeflare.db` or a PostgreSQL URL such as `postgres://user:password@127.0.0.1:5432/nodeflare?sslmode=disable` |
| `bind_addr` | Listen address, `127.0.0.1:2206` by default |
| `admin_username` | Admin username |
| `admin_password` | Admin password (8–128 characters), only needed for the first database initialization and cleared afterwards |
| `trusted_proxies` | Trusted reverse proxy IP / CIDR list; only `X-Forwarded-For` from these networks is trusted |
| `turnstile_site_key` / `turnstile_secret_key` | Turnstile keys; leave empty to disable |
| `session_ttl_hours` | Session lifetime in hours (1–2160), 168 by default |
| `frontend_dir` / `admin_frontend_dir` | Frontend asset directories; relative paths resolve against the configuration file |
| `theme_dir` | Theme extraction directory, `themes` under the configuration directory by default |
A few settings can be overridden on the command line: `nodeflare --config <path> --bind <address> --database <url>`. When started with `--database`, the panel's online migration feature is disabled.
## Default paths and logs
| Platform | Program | Configuration and data |
| --- | --- | --- |
| Linux | `/opt/nodeflare` | `/etc/nodeflare` |
| Windows | `%ProgramFiles%\NodeFlare` | `%ProgramData%\NodeFlare\Server` |
| macOS | `/usr/local/libexec/nodeflare` | `/Library/Application Support/NodeFlare/Server` |
| FreeBSD | `/usr/local/libexec/nodeflare` | `/var/db/nodeflare/server` |
Agent on Linux: program at `/opt/nodeflare/agent`, configuration and state in `/etc/nodeflare/agent`. The SQLite file lives in the configuration directory.
Logs:
- Linux (systemd): `journalctl -u nodeflare -f`; the agent is `journalctl -u nodeflare-agent -f`
- Linux (OpenRC): `rc-service nodeflare status`
- macOS: `/var/log/nodeflare.log`
- Windows: `Get-ScheduledTaskInfo -TaskName nodeflare`
## Reverse proxy
Expose the panel through an HTTPS reverse proxy and add the proxy address to `trusted_proxies`, otherwise every visitor is treated as the same IP and the session cookie is not marked `Secure`:
```toml
trusted_proxies = ["127.0.0.1/32", "::1/128"]
```
nginx:
```nginx
server {
listen 443 ssl;
server_name nodeflare.example.com;
ssl_certificate /etc/letsencrypt/live/nodeflare.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/nodeflare.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:2206;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # WebSocket live data
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
Caddy:
```
nodeflare.example.com {
reverse_proxy 127.0.0.1:2206
}
```
## Database and backups
The "Database" page shows usage, reclaims space, exports and restores ZIP backups, and migrates between SQLite and PostgreSQL. A backup contains settings, nodes, history, notifications, themes, tasks, and security configuration, and requires TOTP or the password. Limits are a 512 MiB ZIP, 4 GiB extracted, 16,384 entries, and 32 MiB per theme file. Migration overwrites the target database, updates the connection string automatically, keeps agent tokens, and takes effect after a restart. History is kept for 30 days by default.
| [Install the Server](https://elysia62.github.io/NodeFlareWiki/en/guide/quick-start.html) | One-line install, first-time setup, script options, updates |
| [Install the Agent](https://elysia62.github.io/NodeFlareWiki/en/guide/agent.html) | Per-platform scripts and options |
| [Uninstall](https://elysia62.github.io/NodeFlareWiki/en/guide/uninstall.html) | Uninstalling the server and agent, data cleanup |
| [Configuration](https://elysia62.github.io/NodeFlareWiki/en/guide/config.html) | Config options and a full example |
| [Reverse Proxy](https://elysia62.github.io/NodeFlareWiki/en/guide/proxy.html) | nginx / Caddy setup and trusted_proxies |
| [Database & Backups](https://elysia62.github.io/NodeFlareWiki/en/guide/database.html) | Backup / restore, SQLite ↔ PostgreSQL migration |
| [Metrics & Sampling](https://elysia62.github.io/NodeFlareWiki/en/guide/monitoring.html) | Sampling rates and memory accounting |
| [Alerts & Notifications](https://elysia62.github.io/NodeFlareWiki/en/guide/alerts.html) | Threshold, offline, expiry, and traffic alerts |
| [Theme Development](https://elysia62.github.io/NodeFlareWiki/en/guide/themes.html) | Theme package layout and data API |
| [FAQ](https://elysia62.github.io/NodeFlareWiki/en/faq.html) | Troubleshooting and frequently asked questions |
| [Development Setup](https://elysia62.github.io/NodeFlareWiki/en/dev/develop.html) | Local development, tests, and builds |
## Development
@@ -239,41 +124,7 @@ cp backend/config.example.toml backend/config.toml
./dev.sh
```
`dev.sh` builds the frontend and starts the backend with `cargo run`. `start.sh` builds release binaries and prefers `/etc/nodeflare/config.toml`, which can be overridden with `NODEFLARE_CONFIG`.
Repository layout:
| Directory | Contents |
| --- | --- |
| `backend/` | Server (Rust / Axum / SQLx): `src/routes` endpoints, `src/db` data layer, `src/websocket` live channels, `migrations/` schema |
| `agent/` | Agent source (collection / upload / remote execution / self-update) and per-platform install scripts |
| `shared/` | Telemetry protocol shared by the agent and the server (serialization + compression) |
| `frontend/` | Frontend (React + Vite): `src/components`, `src/styles` |
| `scripts/` | Build, version resolution, and smoke-test scripts |
| `docs/` | English README and systemd service unit |
Tests and builds:
```bash
bun test --cwd frontend
cargo test --locked --manifest-path backend/Cargo.toml
cargo test --locked --manifest-path agent/Cargo.toml
bun run build
```
Smoke test (start the panel first):
```bash
MONITOR_ADMIN_USERNAME=admin MONITOR_ADMIN_PASSWORD='your-password' bun run test:smoke
```
## Troubleshooting
- **The service fails to start**: check the logs (see [Default paths and logs](#default-paths-and-logs)); usually the port is in use or the database URL is wrong.
- **Unknown listening port**: check `bind_addr` in the configuration file, `127.0.0.1:2206` by default.
- **The agent shows offline**: verify the agent can reach the panel (`curl -I https://your-panel`), check the token, and check the system clock, since reporting and latency rely on clock calibration.
- **Nodes are missing from the public dashboard**: make sure the node is not hidden and that "Public dashboard" is enabled in site settings.
- **Backup export reports the size limit**: shorten the history retention or clear history, then export again.
`dev.sh` builds the frontend and starts the backend with `cargo run`. Tests, builds, and the repository layout are described in [Development Setup](https://elysia62.github.io/NodeFlareWiki/en/dev/develop.html) and [Repository Layout](https://elysia62.github.io/NodeFlareWiki/en/dev/structure.html).
## License
+56
View File
@@ -0,0 +1,56 @@
#!/bin/sh
set -eu
image=${1:-nodeflare:smoke}
: "${NODEFLARE_VERSION:?Set NODEFLARE_VERSION to the expected server version}"
script_dir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)
root_dir=$(CDPATH='' cd -- "$script_dir/.." && pwd)
volume=""
container=""
cleanup() {
if [ -n "$container" ]; then
docker logs "$container" 2>&1 || true
docker rm --force "$container" >/dev/null || true
fi
if [ -n "$volume" ]; then docker volume rm "$volume" >/dev/null || true; fi
}
trap cleanup EXIT
trap 'exit 1' HUP INT TERM
version_output=$(docker run --rm "$image" --version)
[ "${version_output##* }" = "$NODEFLARE_VERSION" ] || {
echo "Unexpected server version: $version_output" >&2
exit 1
}
volume=$(docker volume create)
docker run --rm --user 0:0 --entrypoint sh \
--mount "type=volume,src=$volume,dst=/etc/nodeflare" \
--mount "type=bind,src=$root_dir/docker/config.example.toml,dst=/tmp/config.example.toml,readonly" \
"$image" -c '
set -eu
sed "s/CHANGE_ME_WITH_A_STRONG_PASSWORD/DockerSmokePassword123/" /tmp/config.example.toml > /etc/nodeflare/config.toml
chown -R 10001:10001 /etc/nodeflare
chmod 700 /etc/nodeflare
chmod 600 /etc/nodeflare/config.toml
'
container=$(docker run --detach \
--publish 127.0.0.1::2206 \
--mount "type=volume,src=$volume,dst=/etc/nodeflare" \
"$image")
address=$(docker port "$container" 2206/tcp)
base="http://$address"
curl --noproxy '*' --fail --silent --show-error --retry 30 --retry-connrefused --retry-delay 1 --max-time 3 \
"$base/api/bootstrap" >/dev/null
for path in / /admin/login; do
html=$(curl --noproxy '*' --fail --silent --show-error --max-time 10 "$base$path")
script=$(printf '%s\n' "$html" | sed -n 's/.*src="\([^"]*\.js\)".*/\1/p')
stylesheet=$(printf '%s\n' "$html" | sed -n 's/.*href="\([^"]*\.css\)".*/\1/p')
[ -n "$script" ] && [ -n "$stylesheet" ]
curl --noproxy '*' --fail --silent --show-error --max-time 10 "$base$script" >/dev/null
curl --noproxy '*' --fail --silent --show-error --max-time 10 "$base$stylesheet" >/dev/null
done
docker exec "$container" sh -c '
test -s /etc/nodeflare/nodeflare.db
! grep -q DockerSmokePassword123 /etc/nodeflare/config.toml
'
printf '%s\n' 'Docker image smoke test passed'