FNOS 是一套基于 Debian 发行版开发的 NAS 系统。我的机器除了存储数据,也会通过 SSH 跑一些开发服务和 GPU 容器。这篇文章记录的不是 FNOS 安装过程,而是系统已经装好、SSH 已经开启之后,我怎样把它整理成一台顺手的 Linux 服务器。
这些命令来自我平时维护这台 NAS 的清单。有些只在首次配置时执行一次,有些用于升级后的检查和故障处理。我把它们按用途拆开,避免下次只记得一条命令,却忘了它会改动哪里。
这里会改动 SSH、Swap 和 NVIDIA 驱动等系统配置。FNOS 仍然是一套完整的 NAS 系统,直接修改底层软件包可能影响系统更新、存储服务和 Docker 应用。先确认管理页面可以正常访问,重要数据已有备份,并且机器可以接显示器和键盘进行本地恢复。不要把全文做成一次性脚本执行。
开启 SSH
先在 FNOS 管理页面完成两项设置:
- 在「系统设置 → SSH」中开启 SSH 服务,按自己的网络环境设置端口。
- 在「系统设置 → 用户管理」中找到准备登录的管理员用户,为它启用 SSH 权限。
FNOS v1.1.19 的更新说明明确提到,升级后会同时关闭系统 SSH 服务和用户 SSH 登录权限。只打开第一处开关,客户端仍然无法登录。以后的系统升级如果出现 Connection refused 或登录后立即断开,我会先回管理页面检查这两项,而不是马上修改 sshd_config。
然后从另一台电脑连接。正文中的 HOST 和 TARGET_USER 分别替换为 FNOS 的局域网地址和登录用户:
ssh -p 22 TARGET_USER@HOST登录后先确认当前系统、用户和提权能力:
cat /etc/os-release
uname -a
id
sudo -v后面的 CUDA 仓库以 Debian 12 x86_64 为例。VERSION_ID 或机器架构不同时,不要继续照抄对应章节。
补齐用户主目录
如果 SSH 登录时出现 Could not chdir to home directory,先查看账号记录中的主目录,而不是直接假定它一定是 /home/TARGET_USER:
getent passwd "$USER"
printf 'HOME=%s\n' "$HOME"
ls -ld "$HOME"确认目录确实不存在后再创建:
sudo mkhomedir_helper "$USER"
ls -ld "$HOME"mkhomedir_helper 会按系统账号信息创建主目录并设置属主。目录已经存在时不要重复执行,更不要手工把别人的目录改成当前用户所有。
改用 SSH 公钥登录
先在客户端生成密钥。已有可用密钥时跳过这一步:
ssh-keygen -t ed25519 -a 100
ssh-copy-id -p 22 TARGET_USER@HOSTWindows 自带的 OpenSSH 客户端通常没有 ssh-copy-id,也可以把公钥内容追加到 FNOS 用户的 ~/.ssh/authorized_keys。服务端权限应当是:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys此时不要急着关闭密码登录。新开一个终端,确认下面的连接不再询问账号密码,并且 sudo 仍然可用:
ssh -p 22 TARGET_USER@HOST
sudo -v确认成功后,再写一个独立的 SSH 配置片段:
sudo install -d -m 0755 /etc/ssh/sshd_config.d
sudo tee /etc/ssh/sshd_config.d/99-server-hardening.conf >/dev/null <<'EOF'
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
EOF
sudo sshd -t
sudo systemctl reload ssh
sudo sshd -T | grep -E '^(pubkeyauthentication|passwordauthentication|kbdinteractiveauthentication|permitrootlogin) '只有 sshd -t 没有输出、最后一条命令显示配置已经生效,才算完成。原来的 SSH 窗口先留着,再从新终端登录一次。这样即使配置有误,也能用旧会话撤销文件:
sudo rm /etc/ssh/sshd_config.d/99-server-hardening.conf
sudo systemctl reload ssh如果 FNOS 后续升级重置了 SSH 配置,应以当时版本的管理页面和官方说明为准,不要为了保留这份配置阻止系统更新。
按需创建 Swap
内存充足、负载稳定的机器不一定需要额外 Swap。先看当前状态和系统盘空间:
free -h
swapon --show
df -h /下面创建 4 GiB 的 /swapfile。它应放在系统盘,不要放进 FNOS 的存储池或共享目录:
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon --priority -1 /swapfile
swapon --show确认启用正常后再写入 /etc/fstab,让重启后仍然生效:
grep -qE '^[[:space:]]*/swapfile[[:space:]]+' /etc/fstab || \
echo '/swapfile none swap sw,pri=-1 0 0' | sudo tee -a /etc/fstab
sudo findmnt --verify如果文件系统不支持 fallocate,可以改用较慢但兼容性更好的方式创建文件:
sudo dd if=/dev/zero of=/swapfile bs=1M count=4096 status=progress使用 ZRAM 缓解内存压力
我的 FNOS 还会使用 ZRAM。它在内存里创建压缩交换设备,和磁盘上的 /swapfile 可以同时存在。先检查当前内核是否已经启用 ZRAM:
zgrep CONFIG_ZRAM /proc/config.gz 2>/dev/null || true
lsmod | grep zram || true
zramctl需要配置时安装 systemd-zram-generator:
sudo apt update
sudo apt install systemd-zram-generator
sudoedit /etc/systemd/zram-generator.conf我使用下面这份配置,让 /dev/zram0 的逻辑容量等于物理内存的一半:
[zram0]
zram-size = ram / 2
compression-algorithm = lzo-rlezram-size 是逻辑容量,不代表安装后会立刻占掉一半内存。写入的数据才会消耗内存。配置完成后重启,再检查设备、算法和实际占用:
sudo rebootzramctl
swapon --show
systemctl status dev-zram0.swap --no-pager如果内核不支持 lzo-rle,从下面的输出里选择已经列出的算法,或者删掉 compression-algorithm,使用内核默认值:
cat /sys/block/zram0/comp_algorithm配置 Git 身份
服务器需要直接提交代码时再设置 Git 身份。公开文章不应放个人邮箱,下面使用占位值:
git config --global user.name "YOUR_NAME"
git config --global user.email "YOUR_EMAIL"
git config --global --list如果机器只负责拉取和运行代码,这一步可以省略。仓库需要提交签名、代理或凭据时,也应按仓库单独配置,不要把访问令牌写进全局配置。
数据目录与软链接
FNOS 的数据盘通常挂载在 /vol1。我的开发目录和容器数据都放在数据盘,用户主目录只保留软链接。公开命令使用 STORAGE_ROOT 表示实际的数据目录:
STORAGE_ROOT=/vol1/TARGET_UID
test -d "$STORAGE_ROOT/develop" && ln -s "$STORAGE_ROOT/develop" "$HOME/develop"
test -d "$STORAGE_ROOT/volumes" && ln -s "$STORAGE_ROOT/volumes" "$HOME/volumes"
ls -ld "$HOME/develop" "$HOME/volumes"如果目标路径已经存在,ln 会拒绝覆盖。先判断它是目录、文件还是旧链接,再决定怎么处理,不要直接加 -f。
压缩备份与恢复
临时迁移 volumes 时,我会明确写出源目录和备份目录。这样不会漏掉隐藏文件,也不会把压缩包写进正在打包的目录:
SOURCE_DIR=/vol1/1000/volumes
BACKUP_DIR=/vol1/1000/backups
ARCHIVE="$BACKUP_DIR/volumes-$(date +%Y%m%d-%H%M%S).tar.gz"
mkdir -p "$BACKUP_DIR"
tar -czf "$ARCHIVE" -C "$SOURCE_DIR" .
tar -tzf "$ARCHIVE" | head恢复时不要直接覆盖正在运行的服务目录。我会先解压到空目录,检查内容后再停服务并迁移:
RESTORE_DIR=/vol1/1000/restore-volumes
mkdir -p "$RESTORE_DIR"
tar -xzf "$ARCHIVE" -C "$RESTORE_DIR"
du -sh "$RESTORE_DIR"压缩包能列出文件不等于备份可用。重要数据还需要定期做一次实际恢复,并放一份副本到另一块磁盘或另一台设备。
网络与 Wi-Fi
有线网络异常或临时需要无线连接时,可以用 NetworkManager 查看设备和热点:
nmcli device status
nmcli device wifi list连接 Wi-Fi 时使用交互式输入,避免密码出现在脚本、Shell 历史和进程参数里:
nmcli --ask device wifi connect "WIFI_SSID"连接后检查地址、路由和 DNS:
ip -brief address
ip route
resolvectl statusDocker 用户权限
如果需要让当前用户不加 sudo 运行 Docker,可以加入 docker 组:
sudo usermod -aG docker "$USER"
newgrp docker
docker infodocker 组可以控制 Docker daemon,实际权限接近 root。共享账号或不受信任的 SSH 用户不应加入这个组。
调整系统语言
遇到命令输出乱码、程序缺少所需 locale 时,我会重新选择系统语言:
locale
locale -a
sudo dpkg-reconfigure locales
locale通常保留一个 UTF-8 locale 即可。修改后重新登录 SSH,让新的环境变量进入会话。
可选:Intel 核显与 IOMMU
使用 Intel 核显做媒体转码时,可以安装 VA-API 驱动和诊断工具:
sudo apt update
sudo apt install intel-media-va-driver-non-free intel-gpu-tools vainfo
vainfo
sudo intel_gpu_top软件包是否存在取决于 FNOS 当前使用的 Debian 仓库。如果 apt 找不到包,先检查软件源,不要为了安装一个工具随意混用其他 Debian 版本的仓库。
虚拟机需要 PCIe 设备直通时,我才会启用 Intel IOMMU。先确认处理器和主板支持虚拟化,并在 BIOS 中开启 VT-d,然后编辑 GRUB:
sudoedit /etc/default/grub把参数追加到已有的 GRUB_CMDLINE_LINUX_DEFAULT 中,不要覆盖原来的启动参数:
GRUB_CMDLINE_LINUX_DEFAULT="EXISTING_PARAMETERS intel_iommu=on iommu=pt"生成启动配置并重启:
sudo update-grub
sudo update-initramfs -u -k all
sudo reboot回来后确认参数和 IOMMU 分组已经出现:
cat /proc/cmdline
sudo dmesg | grep -Ei 'DMAR|IOMMU'
find /sys/kernel/iommu_groups/ -type l | head启动失败时需要从 GRUB 临时删除新增参数,或者通过本地终端恢复 /etc/default/grub。远程机器没有带外管理时,我不会只靠 SSH 修改这一项。
可选:NVIDIA CUDA 与 Docker GPU
这一节只适用于装有受支持 NVIDIA 显卡、确实需要 CUDA 或 GPU 容器的机器。FNOS 使用定制内核,驱动模块能否构建和加载取决于当前内核、内核头文件与驱动版本。先记录现场:
lspci -nn | grep -i nvidia
uname -r
nvidia-smi || true
apt-cache policy "linux-headers-$(uname -r)"如果 nvidia-smi 已经正常工作,就不要先清空现有驱动,也不要连续安装两个驱动分支。这样会中断正在使用 GPU 的服务,还可能让定制内核无法加载驱动模块。
以下仓库地址只对应 Debian 12 x86_64。执行前再次核对 /etc/os-release 和 dpkg --print-architecture:
sudo apt update
sudo apt install -y ca-certificates curl wget gnupg
wget https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
rm cuda-keyring_1.1-1_all.deb
sudo apt update先查看仓库实际提供的版本,再决定安装哪一个分支:
apt-cache search '^cuda-drivers-[0-9]+$'
apt-cache search '^cuda-toolkit-[0-9]+-[0-9]+$'我目前使用 CUDA Toolkit 13.2。NVIDIA 的 13.2 发布说明要求 Linux 驱动不低于 595.45.04,因此示例使用 595 分支;实际安装前仍要确认 FNOS 内核、显卡型号和仓库中可用的软件包。这里的变量不是“永远最新”的答案:
DRIVER_BRANCH=595
CUDA_VERSION=13-2
sudo apt install "cuda-drivers-${DRIVER_BRANCH}" "cuda-toolkit-${CUDA_VERSION}"
sudo reboot如果驱动包已经安装,但内核升级后 nvidia-smi 失效,可以先确认 Debian 包状态,再尝试重新安装。这里不是首次安装步骤:
dpkg -l | grep -E 'nvidia-driver|nvidia-driver-cuda'
sudo apt install --reinstall nvidia-driver nvidia-driver-cuda
sudo reboot批量 purge '^nvidia-.*' '^libnvidia-.*' '^cuda-.*' 只留作彻底重装时的最后手段。执行前应停止 GPU 容器、记录已安装包,并先用 apt-get --simulate purge ... 查看会被删除的内容。
重启后验证驱动和编译工具:
nvidia-smi
/usr/local/cuda/bin/nvcc --version如果 nvcc 没有进入当前用户的 PATH,再把配置追加到 ~/.bashrc,并避免重复写入:
grep -qxF 'export CUDA_HOME=/usr/local/cuda' ~/.bashrc || echo 'export CUDA_HOME=/usr/local/cuda' >> ~/.bashrc
grep -qxF 'export PATH=$CUDA_HOME/bin:$PATH' ~/.bashrc || echo 'export PATH=$CUDA_HOME/bin:$PATH' >> ~/.bashrc
grep -qxF 'export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH' ~/.bashrc || echo 'export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc要让 FNOS 的 Docker 使用 GPU,还需要 NVIDIA Container Toolkit。按照 NVIDIA 当前的 Debian 安装方式添加独立仓库:
sudo apt-get update
sudo apt-get install -y --no-install-recommends 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-toolkitnvidia-ctk 会修改 /etc/docker/daemon.json,重启 Docker 也会短暂中断正在运行的容器。先安排维护时间并备份配置:
sudo test ! -f /etc/docker/daemon.json || \
sudo cp -a /etc/docker/daemon.json /etc/docker/daemon.json.before-nvidia
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker最后用一个临时容器验证 GPU。CUDA 镜像标签会被 NVIDIA 定期更新或移除,执行前应在 NGC 的支持标签中确认它仍然存在,并检查主机驱动是否满足对应 CUDA 版本:
docker run --rm --gpus all nvidia/cuda:13.2.0-base-ubuntu24.04 nvidia-smiPostgreSQL 检查
FNOS 上的应用数据库出现问题时,我先看服务状态和近期日志:
systemctl list-units --type=service | grep -i postgres
sudo journalctl -u postgresql -r -n 100 --no-pager服务名称可能不是 postgresql,应使用第一条命令查到的实际 unit。需要进入数据库时:
sudo -u postgres psql在 psql 中可以查看角色,并用交互方式修改密码:
\du
\password postgres
\q我不会执行查询系统表来读取密码哈希,也不会把新密码写进 Shell 命令或文章。
用 snitch 查看网络连接
snitch 是一个把 ss 和 netstat 结果整理成终端界面的第三方工具。直接运行远程安装脚本很方便,但脚本内容会随仓库更新。我会先下载并阅读,再执行本地文件:
curl -fsSLo /tmp/install-snitch.sh \
https://raw.githubusercontent.com/karol-broda/snitch/master/install.sh
less /tmp/install-snitch.sh
sh /tmp/install-snitch.sh
rm /tmp/install-snitch.sh安装后常用的是监听端口和已建立连接:
snitch -l
snitch ls -l
snitch ls -t -e清理 APT 缓存
系统盘空间紧张时,我会先看占用,再清理已下载的软件包:
df -h /
sudo du -sh /var/cache/apt /var/lib/apt/lists
sudo apt-get clean
sudo apt-get autocleanautoremove 可能删掉内核、驱动或其他被判断为不再需要的依赖。先预览,不加 -y:
sudo apt-get autoremove我不再把 rm -rf /var/lib/apt/lists/* 当作日常清理命令。它只会删除软件源索引,下次仍要重新执行 apt update,节省的空间通常不值得多一次手工处理。
日常复查
这套流程做完后,我会保留几条短命令,用来判断系统升级有没有改变运行状态:
systemctl is-active ssh docker
sudo ss -lntp | grep sshd
swapon --show
zramctl
df -h / /vol1
nmcli device status
sudo sshd -t
nvidia-smi没有 NVIDIA 显卡时,最后一条自然不需要执行。机器没有 NetworkManager 或 /vol1 使用了其他挂载点时,也要删掉对应命令。FNOS 升级后则优先检查管理页面中的 SSH 服务和用户权限,再检查自定义 SSH 配置、Swap、ZRAM、数据盘、Docker 与 GPU,不把所有故障都归到网络上。