Installation Guide
This guide walks you through creating a k3s cluster in an internet-connected environment and deploying the full NuFi stack with the install script included in the NuFi installation package you received.
Installation flow
| Step | What you do | Where |
|---|---|---|
| 1. Prerequisites | Check server requirements, install kubectl/Helm | Master node |
| 2. Install the k3s cluster | Install the master node, join worker nodes | All nodes |
| 3. Prepare GPU/NPU nodes | Install drivers and runtime, label nodes | Accelerator nodes |
| 4. Install NuFi | Run install.sh | Master node |
| 5. Verify and access | Check status, set up DNS, log in to the dashboard | Master node + client PC |
At a glance — full command sequence for a single GPU server
# 1. Install k3s (with Traefik disabled)
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik" sh -
mkdir -p ~/.kube && sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown "$USER:$USER" ~/.kube/config && chmod 600 ~/.kube/config
export KUBECONFIG="$HOME/.kube/config"
# 2. Install the NVIDIA driver + Container Toolkit (see step 3),
# then set nvidia as the default k3s runtime
sudo mkdir -p /etc/rancher/k3s
echo 'default-runtime: nvidia' | sudo tee /etc/rancher/k3s/config.yaml
sudo systemctl restart k3s
kubectl label node "$(hostname)" nvidia.com/gpu.present=true
# 3. Install NuFi (after extracting the bundle)
unzip nufi-helm-bundle.zip && cd nufi-helm
./scripts/install.sh \
--base-domain nufi.example.com \
--registry-server registry.dudaji.com \
--registry-username <username> \
--registry-token <token>
See the step-by-step sections below for what each command does and how to verify it.
1. Prerequisites
Server requirements
| Item | Requirement |
|---|---|
| OS | Ubuntu 22.04 LTS or later (x86_64) |
| CPU | 4 cores or more |
| RAM | 16GB or more |
| Disk | 200GB or more |
| Network | Access to the NuFi image registry |
| Ports | 80 (HTTP) and 443 (HTTPS) must be free on the master node |
The NuFi ingress gateway binds directly to ports 80/443 on the master (control-plane) node using hostNetwork. This lets you reach NuFi at the master node IP without a LoadBalancer. If you need different ports, see --external-http-port and --external-https-port under Install options.
Install kubectl and Helm
Install kubectl and Helm on the master node. Prefer the latest instructions in the kubectl official docs and Helm official docs.
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg openssl nfs-common
# Helm
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
rm kubectl
Verify
kubectl version --client
helm version
Prepare the install files
Copy the NuFi installation package (nufi-helm-bundle.zip) you received to the master node and unzip it. The extracted directory is all you need for the installation.
unzip nufi-helm-bundle.zip
cd nufi-helm
nufi-helm/
├── scripts/install.sh # install script
├── scripts/uninstall.sh # uninstall script
├── values/ # default values per chart, Keycloak Realm file (realm-export.json)
└── charts/ # NuFi local charts
If you use a private registry, also prepare the registry server address, username, and token.
2. Install the k3s cluster
2-1. Master node
k3s installs the Traefik ingress controller by default. Traefik occupies ports 80/443 and conflicts with NuFi's Istio ingress gateway, so you must disable Traefik when installing.
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik" sh -
Copy the kubeconfig so you can use kubectl as a regular user.
mkdir -p ~/.kube
sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
sudo chown "$USER:$USER" ~/.kube/config
chmod 600 ~/.kube/config
export KUBECONFIG="$HOME/.kube/config"
Verify
kubectl get nodes
kubectl get storageclass
| Item | Expected |
|---|---|
| Node | The master node is Ready |
| StorageClass | local-path is shown as (default) |
| Traefik | kubectl -n kube-system get helmchart traefik must return nothing. If it remains, reinstall k3s with --disable=traefik. |
| Ports | sudo ss -ltnp shows no process occupying 80/443 |
Advanced: use Cilium CNI instead of flannel
To use Cilium instead of the default flannel, disable flannel and network policy when installing k3s, then install Cilium. The default k3s Pod CIDR is 10.42.0.0/16.
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--flannel-backend=none --disable-network-policy --disable=traefik" sh -
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
cilium install --version 1.19.4 --set=ipam.operator.clusterPoolIPv4PodCIDRList="10.42.0.0/16"
cilium status --wait
See the Cilium k3s installation docs for details.
2-2. Join worker nodes (optional)
Skip this step for a single-server setup.
We recommend installing the driver and NVIDIA Container Toolkit first (see 3. Prepare GPU/NPU nodes) before joining. Installing after joining also works, but then you need to restart k3s-agent after configuring the runtime.
Check the join token on the master node.
sudo cat /var/lib/rancher/k3s/server/node-token
Join from the worker node with the master IP and token.
export MASTER_IP=<master node IP>
export NODE_TOKEN=<token from above>
curl -sfL https://get.k3s.io | K3S_URL="https://${MASTER_IP}:6443" K3S_TOKEN="${NODE_TOKEN}" sh -
Verify — on the master node:
kubectl get nodes
The new worker node is joined once it shows Ready. CNI initialization can take 1–2 minutes.
3. Prepare GPU/NPU nodes
Run this on every node with an accelerator. If you have no accelerators, skip this step and pass --nvidia-enabled false when installing NuFi.
- NVIDIA GPU
- FuriosaAI NPU
3-1. Install the NVIDIA driver
sudo apt-get update
ubuntu-drivers devices # check the recommended version
sudo apt install <driver package marked as recommended>
Verify
nvidia-smi
3-2. Install the NVIDIA Container Toolkit
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg2
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
| sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
| sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
3-3. Set nvidia as the default k3s runtime
Set the default runtime to nvidia so GPU containers can use GPUs without extra configuration.
sudo mkdir -p /etc/rancher/k3s
cat <<'EOF' | sudo tee /etc/rancher/k3s/config.yaml
default-runtime: nvidia
EOF
Restart the k3s service that matches the node role.
# Worker node
sudo systemctl restart k3s-agent
# Master node with a GPU installed
sudo systemctl restart k3s
Verify
sudo grep -n "default_runtime_name\|BinaryName" /var/lib/rancher/k3s/agent/etc/containerd/config.toml
You should see default_runtime_name = "nvidia". If not, delete the containerd config generated by k3s so it is recreated, then restart.
sudo rm -rf /var/lib/rancher/k3s/agent/etc/containerd
sudo systemctl restart k3s-agent # use k3s on the master
3-4. Label GPU nodes
Label the GPU nodes from the master node.
kubectl label node <gpu-node-name> nvidia.com/gpu.present=true
Nodes running FuriosaAI RNGD require Ubuntu 22.04 LTS or Debian Bookworm or later, with Linux kernel 6.3 or later. See FuriosaAI prerequisites for details.
Check the device
sudo apt update && sudo apt install -y pciutils
sudo update-pciids
lspci -nn | grep FuriosaAI
Install the driver
sudo apt update && sudo apt install -y curl gnupg
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg \
| sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/cloud.google.gpg
echo "deb [arch=$(dpkg --print-architecture)] http://asia-northeast3-apt.pkg.dev/projects/furiosa-ai $(. /etc/os-release && echo "$VERSION_CODENAME") main" \
| sudo tee /etc/apt/sources.list.d/furiosa.list
sudo apt update
sudo apt install -y build-essential linux-modules-extra-$(uname -r) linux-headers-$(uname -r)
sudo apt install -y furiosa-driver-rngd furiosa-smi
Verify
furiosa-smi info
Pass --furiosa-enabled true when installing NuFi.
4. Install NuFi
On the master node, move into the bundle directory and run the install script. It is safe to re-run; running the same command again acts as an upgrade.
cd nufi-helm
chmod +x scripts/install.sh
Pick the command that matches your environment.
- GPU server (default)
- Furiosa NPU server
- CPU only (for validation)
./scripts/install.sh \
--base-domain nufi.example.com \
--registry-server registry.dudaji.com \
--registry-username <username> \
--registry-token <token>
The NVIDIA device plugin and DCGM exporter are installed together (--nvidia-enabled defaults to true).
./scripts/install.sh \
--base-domain nufi.example.com \
--registry-server registry.dudaji.com \
--registry-username <username> \
--registry-token <token> \
--furiosa-enabled true
For NPU-only servers without GPUs, also add --nvidia-enabled false.
./scripts/install.sh \
--base-domain nufi.example.com \
--nvidia-enabled false \
--furiosa-enabled false
You can omit the registry options if your environment does not require registry authentication.
Pass only the domain to --base-domain, without a port. After installation you will access NuFi at dashboard.<domain>, api.<domain>, keycloak.<domain>, and grafana.<domain>.
The default NuFi Dashboard login account is admin / nufiadmin. Do not keep the default password in production.
The dashboard NextAuth secret is generated automatically on first install and preserved on later re-runs. Set NEXTAUTH_SECRET only when you need to rotate it.
What gets installed
After preparing namespaces, Secrets, and CRDs, the script installs Helm releases in the following order.
| Component | Releases | Role |
|---|---|---|
| Istio | istio-base, istiod, istio-gateway | Ingress gateway, service routing |
| Keycloak | keycloak, keycloak-admin-password | Authentication/SSO (realm super-llm imported automatically) |
| Monitoring | prometheus, loki, nufi-monitoring | Metrics/log collection, Grafana dashboards |
| Autoscaling | keda | Serving autoscaling |
| Storage | csi-driver-nfs, juicefs-minio, juicefs-meta, juicefs-csi, juicefs-config | Shared volume storage |
| Devices (optional) | nvidia-device-plugin, dcgm-exporter / 3 furiosa-* releases | GPU/NPU resource exposure, metrics |
| DNS | coredns-custom | In-cluster *.<domain> routing |
| Notebook | notebook-controller | Lab (Jupyter) controller |
| NuFi applications | nufi-controller, nufi-api-server, dashboard | NuFi core |
Install options
Commonly used options. Run ./scripts/install.sh --help for the full list.
| Option | Description | Default |
|---|---|---|
--base-domain | Base domain for NuFi services (no port) | nufi.local |
--registry-server / --registry-username / --registry-token | Private registry credentials. When set, an imagePullSecret is created in every namespace. | none |
--nufi-version | Image tag for the NuFi applications | versions.nufi in values/bundled.yaml |
--nvidia-enabled | Whether to install the NVIDIA device plugin and DCGM exporter | true |
--furiosa-enabled | Whether to install the Furiosa NPU components | false |
--external-http-port / --external-https-port | External HTTP/HTTPS ports. If changed, include the port in access URLs. | 80 / 443 |
--tls-enabled | Whether to install the HTTPS gateway and cert-manager. Use only when certificates are prepared. | false |
--storage-size | Shared storage (JuiceFS) PVC size | 100Gi |
--apps-only | Skip dependency releases and upgrade only the 3 NuFi applications | false |
--skip-repo-update | Skip refreshing already-configured Helm repositories | false |
Re-run with a longer timeout, e.g. HELM_TIMEOUT=30m ./scripts/install.sh .... Re-running is always safe.
5. Verify and access
5-1. Check status
helm list -A
kubectl get pods -A
Confirm the Pods in the core namespaces are Running or Completed.
kubectl -n istio-system get pods
kubectl -n keycloak get pods
kubectl -n monitoring get pods
kubectl -n nufi get pods
kubectl -n kubeflow get pods
Confirm GPU/NPU nodes expose their resources.
# NVIDIA
kubectl describe node <gpu-node-name> | grep -i nvidia.com/gpu
# Furiosa
kubectl describe node <npu-node-name> | grep -i furiosa
5-2. Set up DNS
NuFi uses fixed hosts like dashboard.<domain> together with dynamic hosts created per Serving, so dynamic hosts under <domain> must resolve to the master node IP.
| Environment | Method |
|---|---|
| Corporate/production network | Register wildcard records under <domain> pointing to the master node IP on your internal DNS server |
| Access from a personal PC | Follow the DNS Setup Guide to configure local DNS on the client PC |
For quick client-only access setup, use the NuFi-provided nufi-access script.
# macOS/Linux
curl -LsSf https://docs.nufi.me/install/nufi-access.sh | sudo bash -s -- --mode wildcard --base-domain <domain> <master node IP>
# Windows PowerShell
# If Chocolatey is not installed, install it first by following the DNS Setup Guide.
powershell -NoProfile -ExecutionPolicy Bypass -Command "& ([scriptblock]::Create((irm https://docs.nufi.me/install/nufi-access.ps1))) -Mode wildcard -BaseDomain <domain> -NodeIp <master node IP>"
See the DNS Setup Guide for Chocolatey installation and cleanup commands on Windows.
5-3. Access
curl "http://api.<domain>/api/v1/healthz"
Open the dashboard in a browser.
http://dashboard.<domain>
| Service | URL | Default account |
|---|---|---|
| Dashboard | http://dashboard.<domain> | admin / nufiadmin |
Upgrade and uninstall
Upgrade
Run the same command you used for installation. To move to a new NuFi version, change only --nufi-version. To upgrade just the NuFi applications quickly:
./scripts/install.sh --base-domain <domain> --nufi-version <new version> --apps-only
Uninstall
./scripts/uninstall.sh
This removes the Helm releases in reverse install order. CRDs, PVCs, Secrets, and namespaces are preserved, so data survives a reinstall.
To remove the k3s cluster itself:
# Master node
sudo /usr/local/bin/k3s-uninstall.sh
# Worker node
sudo /usr/local/bin/k3s-agent-uninstall.sh
Troubleshooting
| Symptom | What to check |
|---|---|
No default StorageClass found | Check for a (default) StorageClass with kubectl get sc. The k3s default is local-path. |
Keycloak realm export not found | Confirm values/realm-export.json is included in the install bundle. |
| Cannot access the dashboard | Confirm the wildcard DNS resolves to the master node IP and the gateway is Running via kubectl -n istio-system get pods. |
| Image pull failures | Check the registry server/username/token, then inspect Events in kubectl -n nufi describe pod <pod>. |
| NVIDIA resources not visible | Check nvidia-smi, the runtime setup in 3-3, and the kubectl -n kube-system get ds nvidia-device-plugin status. |
| Furiosa resources not visible | Confirm furiosa-smi info succeeds on the host and that you installed with --furiosa-enabled true. |
| Helm install timeout | Increase the timeout with HELM_TIMEOUT=30m ./scripts/install.sh ..., then inspect slow Pods with kubectl describe pod and kubectl logs. |
Worker node never becomes Ready | Confirm the firewall allows worker→master TCP 6443 and UDP 8472 (flannel), and check journalctl -u k3s-agent -f. |
Redo a worker node join from scratch (clean up leftovers)
If a join went wrong, clean up the k3s and network leftovers on the worker, then rejoin.
# On the worker node
sudo systemctl stop k3s-agent || true
sudo /usr/local/bin/k3s-agent-uninstall.sh || true
# Clean up leftover network interfaces
sudo ip link delete flannel.1 2>/dev/null || true
sudo ip link delete cni0 2>/dev/null || true
sudo ip link delete kube-ipvs0 2>/dev/null || true
# Clean up k3s/CNI leftovers
sudo rm -rf /etc/rancher/k3s /etc/rancher/node /var/lib/rancher/k3s \
/var/lib/cni /etc/cni/net.d /run/flannel /var/run/flannel
# Reboot to fully clear kernel network state
sudo reboot
If the node still appears on the master, delete it and rejoin.
kubectl delete node <worker-node-name>
Other cluster environments
kind (local validation/CI only)
kind is suitable for local validation and CI smoke tests; it is not a production path.
kind create cluster --name nufi-helm
./scripts/install.sh --base-domain nufi.local --nvidia-enabled false --furiosa-enabled false
install.sh auto-detects kind clusters and applies kind-specific values that disable the gateway's hostNetwork. After installation, access the dashboard via port-forward.
sudo kubectl -n istio-system port-forward svc/istio-ingressgateway 80:80 --address 0.0.0.0
When configuring DNS, use 127.0.0.1 as the NuFi server IP.
Existing Kubernetes cluster
What to check when using an existing cluster:
| Item | Expected |
|---|---|
| kubeconfig | kubectl get nodes and kubectl get ns work with admin permissions |
| Default StorageClass | At least one (default) StorageClass in kubectl get sc |
| Gateway scheduling | Pods can be scheduled on a node labeled node-role.kubernetes.io/control-plane=true |
| External access | Ports 80/443 on the gateway node are reachable from user PCs |
Environments like managed Kubernetes — where workloads cannot run on control-plane nodes or hostNetwork port binding is restricted — may not match the default install values. Decide on the gateway exposure method and ports first, then pass --external-http-port and --external-https-port accordingly.
Next steps
- Create a user workspace in Project Management and review the model deployment flow in Serving.