新增docker支持
This commit is contained in:
1 parent
0ef99ec44d
commit
37d8c663c7
8 files changed
+437
-219
No files matched your search
@@ -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
|
||||
@@ -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]}"
|
||||
@@ -9,6 +9,9 @@
|
||||
/frontend/admin-dist/
|
||||
/release/
|
||||
|
||||
# Docker 持久化数据
|
||||
/data/
|
||||
|
||||
# 依赖
|
||||
/node_modules/
|
||||
/frontend/node_modules/
|
||||
|
||||
+52
@@ -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"]
|
||||
@@ -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
|
||||
```
|
||||
|
||||
## 文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
|
||||
@@ -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
@@ -1,21 +1,17 @@
|
||||
# NodeFlare
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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'
|
||||
Reference in new issue
Block a user