☰
旧笔记本改造无头Ubuntu服务器:从重装系统到SSH加固与Docker部署全记录
2026/9/26 12:18:36 网站建设 项目流程

龙虾尝鲜记这个系列写到第4期,前几期围观了不少新鲜玩意儿,这次终于轮到一台真正要长跑的机器了。当年囤的旧笔记本,屏幕边缘已经出现竖线,键盘也有几个键失灵,但处理器和内存其实都很健康。扔了可惜,卖了不值钱,干脆把它改造成一台无头的Ubuntu服务器。所谓无头,就是不接显示器、不接键盘鼠标,所有操作都从网络远程完成。这篇文章完整记录我从重装系统、网络配置、SSH登录加固,到Git、Node.js、MySQL、Docker等一套常用开发服务的落地全过程。如果你也有闲置电脑,或者想弄明白一台真正的Linux服务器是怎么从零养成型的,这篇很适合当一份可以直接照着做的作业。

1. 无头化之前:观念与硬件的准备

1.1 笔记本做服务器,到底图什么

很多人一听"服务器"三个字,脑子里先蹦出来的是机架、机房、空调这些重装备。其实在家庭和小团队环境里,一台普通x86笔记本做服务器非常合适,而且有台式机和云主机都没有的优势。

首先是自带UPS。笔记本的电池在城市供电不稳或者意外断电的时候,就是一台天然的不间断电源。云主机断电几个月都没事,实体机器最怕断电,用台式机做服务器往往还要另配UPS,一台靠谱的UPS少说也贵过一台二手本。笔记本只要设置好电源策略,断电后能自动触发正常关机流程,来电后又能自动开机,比台式机省心太多。

其次功耗低。一台15到30瓦的笔记本,满负荷运行的功耗也就是台式机的零头。家用服务器24小时不关机,电费是长期账单,这一点差异一年能省出不少钱。实测下来,合盖状态下靠WiFi跑轻量服务,整机功耗能压在5到8瓦,比一盏台灯还省电。

再一个硬件优势容易被忽略:笔记本自带屏幕、键盘、触控板。虽然无头服务器的日常操作都走网络,但真到救急的时候,比如网络配置写错导致SSH连不进来,身上有键盘屏幕就等于有了一套应急终端,直接开机盖就能现场抢救。

缺点也摆在这里。散热和硬盘寿命是主要隐患。笔记本结构紧凑,长期高温工作会加速硅脂老化,机械硬盘在震动环境下也容易出问题。所以强烈建议用固态硬盘做系统盘,外壳可以垫个支架加强散热。我对这台机器的定位很明确:一台跑开发服务、内网共享、定时任务的皮实小机器,不追求极致性能,稳定压倒一切。

1.2 装机前的三个决定

任何一个有点规模的项目,开头做决定比埋头敲命令更重要,无头服务器也不例外。

第一个决定是版本。我选了Ubuntu 22.04 LTS而不是24.04,原因很直白。22.04作为LTS版本已经在生产环境里跑了很久,各种第三方软件、驱动、Docker镜像的兼容信息都非常全,遇到问题时能搜到的答案,大半都是基于22.04的。而24.04虽然内核和用户态软件更新,但一些老工具、老项目在上面会踩编译和依赖的坑。对于一台无人值守的服务器,稳定和可查比新潮重要得多。

第二个决定是磁盘分区。安装的时候直接选了"使用整个磁盘并设置LVM",没有做加密。LVM的好处是后续想扩容分区不需要重新划分物理分区,对长期使用的服务器很重要。没做加密是因为这台机器放在家里,物理接触少,如果真遇到极端情况,加密的盘在开机时还要手动输入密码短语,无头模式没法远程输,只能开机盖操作,这就违背了无人值守的初衷。所以这次不加密。

第三个决定是账号规划。我建了一个普通用户叫lobster,后续所有日常服务都用这个账号跑,不直接用root。主机名设为lobster-box,这个名字虽然随意,但后面mDNS解析、SSH登录提示、日志来源都会用到,提前想清楚能省不少麻烦。

1.3 安装完成后立刻要做的事

系统装好以后,第一波配置要趁热做完,避免后面忘掉。按这个顺序来:

sudo apt update && sudo apt upgrade -y sudo apt install -y vim curl wget git tmux net-tools htop sudo timedatectl set-timezone Asia/Shanghai sudo hostnamectl hostname lobster-box

更新系统这个动作必须放在最前面,因为旧系统镜像里的软件源索引和软件包版本都已经过时,直接装新东西很容易遇到依赖版本对不上。基础工具里tmux和htop对无头服务器尤其重要,tmux是终端复用器,SSH断开后会话不会丢,htop则是监控CPU和内存最直观的工具。时区设置很多人会忽略,但不设的后遗症很直接:服务器日志里的时间戳全部是UTC,排查问题时日志时间和自己感知的"现在"对不上,经常把自己绕晕。

如果后面要编译软件,建议顺手把构建工具链也装了。我在另一台机器上遇到过gcc编译失败的问题,后来发现是缺build-essential和内核头文件,一条命令解决:

sudo apt install -y build-essential linux-headers-$(uname -r)

2. 让它"无头":关掉一切不需要的东西

2.1 关闭图形界面

装完Ubuntu桌面版后,系统默认进入图形界面,一堆GNOME组件常驻内存。但这台机器不需要屏幕输出,也不打算接显示器用图形桌面,所以要关掉图形界面,让系统启动后直接进入纯命令行多用户模式。

sudo systemctl set-default multi-user.target

这条命令把默认启动目标从graphical.target改为multi-user.target。执行后重启,系统就不再启动显示管理器,直接停留在干净的文本登录界面。效果立竿见影,原来桌面版空载内存占用动辄1.5GB以上,关掉图形界面后能压到700MB以内,CPU也干净了,没有各种GNOME后台进程频繁唤醒。对于一台内存可能只有8GB甚至4GB的旧笔记本来说,省下来的资源就是实打实的服务容量。

项目图形模式无头命令行模式
空载内存占用1.2~1.5GB600~800MB
非必要进程GNOME相关组件基本没有
启动到可登录耗时30秒左右15秒以内

如果哪天真需要图形界面救急,切换回去也只靠一条命令:

sudo systemctl set-default graphical.target sudo reboot

切来切去不影响系统里的图形软件包,它们只是不启动而已。

2.2 禁止休眠与合盖挂起

这个配置是笔记本做无头服务器的核心工程。Linux桌面版默认的策略是:合上盖子就挂起,一段时间无操作就自动休眠。但服务器要求7x24在线,这些策略一个都不能留。

先禁用系统级睡眠目标:

sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

mask操作比disable更彻底,等于在systemd的目标列表里把睡眠目标指向空设备,任何服务想触发休眠都会失败。同时还要修改logind.conf,让合盖动作不再挂起:

sudo sed -i 's/#HandleLidSwitch=suspend/HandleLidSwitch=ignore/' /etc/systemd/logind.conf sudo systemctl restart systemd-logind

之所以单独处理合盖开关,是因为很多笔记本"合盖即挂起"是直接由ACPI事件触发的,仅屏蔽systemd睡眠目标还不够,必须让logind忽略合盖事件。配置好后,哪怕笔记本盖子合上长期丢在角落,系统该跑的服务都照常运行。

电源按钮也要处理一下。默认短按电源按钮会挂起,我在logind.conf里把HandlePowerKey改成ignore,防止不小心碰到电源键导致系统进入睡眠态。

注意:修改logind.conf后要重启systemd-logind,否则合盖策略不会即时生效。如果是通过SSH远程配置,做完这一步要确认当前会话没被误杀,免得把自己关在门外。

2.3 网络:从DHCP到静态IP

服务器没有固定IP,就像人没有固定地址,别人想找也找不到。安装时Ubuntu默认走DHCP,路由器分配的IP可能随重启变化,对无头机器来说这是致命伤。连IP都找不到,怎么SSH进去?

Ubuntu 22.04的网络配置文件是netplan,位置在/etc/netplan/下面。我先查看当前网卡叫什么:

ip link

常见的有eno1、eth0这种有线网卡名,或者wlp2s0这种无线网卡名。把配置文件改成下面这个样子(有线网卡示例):

network: version: 2 ethernets: eth0: dhcp4: no addresses: - 192.168.1.100/24 routes: - to: default via: 192.168.1.1 nameservers: addresses: - 192.168.1.1 - 223.5.5.5

写的时候最需要注意的是YAML缩进,netplan对缩进极其敏感,空格多了少了应用时会直接报错。改完执行:

sudo netplan apply

如果这台笔记本要连WiFi,netplan同样支持:

network: version: 2 wifis: wlp2s0: dhcp4: no addresses: - 192.168.1.100/24 routes: - to: default via: 192.168.1.1 nameservers: addresses: - 192.168.1.1 access-points: "your-wifi-ssid": password: "your-wifi-password"

能用有线的场景,我强烈建议用有线。无线网卡的传输效率和稳定性受墙壁、邻居信号、2.4GHz频段拥堵影响很大,对需要长时间维持SSH会话的服务来说,有线省心得多。这台笔记本正好有千兆网口,直接插上,把地址固定在192.168.1.100,后续所有服务都围绕这个IP展开。

3. 远程连接与日常管理

3.1 装好SSH并加固

无头服务器的生命线就是SSH。没有SSH,这台机器就是一块砖头。Ubuntu Server版默认自带openssh-server,但Desktop版不会默认装,所以第一步手动安装:

sudo apt install -y openssh-server sudo systemctl enable --now ssh

检验服务有没有起来:

systemctl status ssh --no-pager -l

然后做基础加固,编辑/etc/ssh/sshd_config,核心就改这几个地方:

  • PermitRootLogin改成no,禁止root直接登录,日常用普通用户登录,需要高权限时再sudo。
  • PasswordAuthentication先保持yes,等公钥登录验证通过后再关闭,避免把自己锁在门外。
  • Port不要急着改。默认22端口在家庭内网里没什么风险,改成高位端口看起来安全,但容易忘,而且只要密钥足够强,22端口照样安全。以后如果这台机器要暴露到公网,再认真考虑端口和Fail2ban这些手段。

改完配置记得校验并重载:

sudo sshd -t sudo systemctl restart ssh

sshd -t这一步很多人不做,但它能在重载前先检查语法错误,配置写坏了不至于当场断连。凡是改网络、改SSH这类"改完可能断连"的配置,都先做干跑测试,这是生产环境养成的习惯。

3.2 公钥登录配置

密码登录虽然方便,但暴力破解的威胁始终存在,而且每次登录敲密码也比密钥麻烦得多。我在客户端电脑上生成一对SSH密钥,然后把公钥推到服务器上:

ssh-keygen -t ed25519 -C "lobster-headless" ssh-copy-id lobster@192.168.1.100

现在主流推荐ed25519而不是RSA,它的密钥长度更短,安全强度相当,而且全程在本地客户端生成,私钥不会离开本地机器。ssh-copy-id会自动把公钥追加到服务器上~/.ssh/authorized_keys里,并设置好权限。

做完这个先验证一下能不能免密登录:

ssh lobster@192.168.1.100

确认没问题后,再回到服务器上关闭密码登录:

sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config sudo systemctl restart ssh

从此这台无头服务器只能通过密钥登录,暴力破解的窗口基本关死。

注意:关闭密码登录前,务必先在另一个终端窗口用密钥登录测试一次,确认稳定通过再执行。你要是把自己锁在外面,就只能跑机房按重置键了。

3.3 无头之后,我靠什么管理日常

没有桌面的世界里,一切都是命令行,但这也意味着一切都能自动化。我慢慢积累了几套顺手的做法。

第一是tmux常驻会话。每次SSH进去都要重新执行工作区里的所有命令很烦。我用tmux新建会话,所有长时间运行的任务都挂在里面,SSH断线也不影响任务执行,重连后随时回到原会话继续查看。

tmux new -s work tmux attach -t work

第二是用systemd托管自定义服务。比直接用nohup启动服务干净得多,不仅能设置开机自启,崩溃了还能自动重启。比如我写的一个定时任务脚本,放在/usr/local/bin/backup_job.sh,然后用一个简单的systemd服务文件管理它:

[Unit] Description=My Backup Job [Service] ExecStart=/usr/local/bin/backup_job.sh Restart=always User=lobster [Install] WantedBy=multi-user.target

第三是给自己写了一套"一台机器管全家"的辅助脚本。比如想一次性看到CPU、内存、磁盘和运行中服务,用一行组合命令:

alias mz='echo "=== CPU ==="; top -bn1 | head -5; echo "=== MEM ==="; free -h; echo "=== DISK ==="; df -h; echo "=== ACTIVE SERVICES ==="; systemctl list-units --type=service --state=running --no-pager | head -20'

这些配置都写进~/.bashrc。长期下来,这台机器用起来的感觉和自己常用的电脑没有区别,反而因为不需要鼠标键盘,操作节奏更专注。

4. 开发环境四件套

4.1 Git:不光是装完就完事

无头服务器最常见的用途是当开发机或者代码仓库,Git是基础中的基础。

sudo apt install -y git git config --global user.name "lobster" git config --global user.email "lobster@example.com"

全局配置很重要,不配置的话每次commit都会提示你"please tell me who you are",很烦。如果这台服务器还需要从远程仓库拉代码,可以生成一对部署密钥:

ssh-keygen -t ed25519 -C "lobster-deploy" cat ~/.ssh/id_ed25519.pub

把公钥添加到代码托管平台的Deploy Keys或SSH Keys里,以后git clone、git pull就不需要每次输密码。在服务器上维护项目时,我习惯给每个项目都加上.gitignore,把临时文件、敏感配置都挡在提交之外。特别是.env这类含密钥的配置,漏提交了就麻烦大了。命令行环境下尤其要注意,因为git status不会像IDE那样弹出可视化警告。

4.2 Node.js:用nvm而不是apt

Ubuntu的apt源里虽然有Node.js,但版本往往落后不少,而且想同时运行多个Node版本时,apt根本没法优雅处理。我直接装了nvm来管理:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash source ~/.bashrc nvm install 20 nvm use 20

装完验证:

node -v npm -v

nvm的好处非常具体:不同项目依赖不同版本的Node,nvm install和nvm use就能秒切;卸载也干净,不会污染系统级的/usr目录。npm registry这一块,在家庭或内网环境里直接用默认官方源基本没压力,如果拉包慢,按自己实际的网络条件配置合适的registry地址即可。反正端口默认对着外网,只要网络畅通问题都不大。

4.3 MySQL:8.0的坑与基础配置

数据库选了MySQL,因为团队已有的项目都跑MySQL,兼容性最省心。Ubuntu 22.04官方源里自带MySQL 8.0,一条命令装好:

sudo apt install -y mysql-server sudo systemctl enable --now mysql

MySQL 8.0和5.7的最大区别在于默认用户认证插件换成了caching_sha2_password,早期很多老客户端的驱动不支持这个插件,连接时报authentication plugin错误。我的建议是,如果客户端工具比较老,就给远程用户显式指定mysql_native_password,确保连接顺畅。

初始配置建议跑一遍安全脚本:

sudo mysql_secure_installation

它会引导你设置root密码强度、删除匿名用户、禁止root远程登录等一系列安全项。默认MySQL只绑定127.0.0.1,服务器上其他机器访问不了。如果希望内网里的其他电脑能连上来,我打开/etc/mysql/mysql.conf.d/mysqld.cnf,把bind-address改成本机IP。然后建业务库和远程账号:

CREATE DATABASE lobster_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'app'@'192.168.1.%' IDENTIFIED WITH mysql_native_password BY 'StrongPass2024!'; GRANT ALL PRIVILEGES ON lobster_app.* TO 'app'@'192.168.1.%'; FLUSH PRIVILEGES;

utf8mb4这个字符集几乎是必选项,否则Emoji和部分生僻字会保存失败。授权语句里我用了192.168.1.%来表示只允许内网网段访问,不给外部留缝隙。

提示:MySQL字符集务必在创建库时就指定utf8mb4,后期再改库和表的字符集,各种索引长度和乱码问题会纠缠很久。

4.4 Docker:容器化的起点

Docker在现代服务器上基本是标配。Ubuntu自带的docker.io包版本够用,安装也最简单直观:

sudo apt install -y docker.io docker-compose-v2 sudo systemctl enable --now docker sudo usermod -aG docker lobster

把当前用户加进docker组后,需要退出SSH重新登录才会生效。不加组也可以,但每次sudo docker很烦,加进去后日常操作更流畅。装好后先用一个最简单的容器验证一下:

docker run -d --name test-nginx -p 8080:80 nginx:alpine

等几十秒,curl一下本机8080端口:

curl -I http://192.168.1.100:8080

返回200 OK,就说明Docker的网络、存储、守护进程全都正常。确认后停掉删除:

docker rm -f test-nginx

把常用服务容器化之后,这台笔记本的部署方式就完全现代化了。以后想加服务,先docker search找镜像,再docker run拉起来,完全不污染宿主机环境。唯一要提醒的是笔记本磁盘空间有限,docker镜像只会越积越多,要养成定期清理的习惯,比如每周跑一次docker image prune。

5. 我踩过的坑与解决实录

5.1 忘记密码:单用户模式重置

无头服务器最惊险的事情之一就是一段时间没登录,把密码忘了。这个情况我在一台实验机器上专门演练过一次,方法很简单但很容易手忙脚乱。

重启服务器,开机时在GRUB菜单上按e进入编辑模式,找到以linux开头的引导行,在末尾加上:

init=/bin/bash

按Ctrl+X启动进入单用户bash。因为系统以只读方式挂载,需要先重新挂载根分区:

mount -o remount,rw / passwd lobster

然后exit或按Ctrl+Alt+Delete重启,密码就重置成功了。这套操作在有键盘屏幕的笔记本上非常顺手,如果是纯远程无头环境就麻烦多了。这也侧面说明,用带键盘屏幕的笔记本做无头服务器有多合理的理由——救急就是成本。

5.2 显卡驱动卸载不掉

这台旧笔记本的独显驱动在无头模式下完全是多余的,因为服务器不做图形输出,GPU基本不参与任何工作。但Ubuntu默认的"附加驱动"里如果装过NVIDIA闭源驱动,卸载起来就费劲了。

我遇到的典型报错是卸载时dkms报"Failed to install/removing",或者提示kernel module还在使用。解决思路是先把驱动服务停掉再卸:

sudo systemctl stop nvidia-persistenced sudo apt purge -y nvidia-* sudo apt autoremove -y sudo rm -f /etc/modprobe.d/nvidia*.conf

然后要禁用默认的开源驱动nouveau,否则重启后系统会自动加载干扰。在/etc/modprobe.d/blacklist-nouveau.conf里写入:

blacklist nouveau options nouveau modeset=0

更新initramfs后重启:

sudo update-initramfs -u sudo reboot

这条路径走下来,显卡驱动才算彻底干净。最稳的做法其实是从一开始就不要在无头服务器上装闭源驱动,集成显卡或者内核开源驱动足够撑起系统。

5.3 环境变量写错,命令全没了

有次我在配置Java环境的时候,手滑把PATH变量覆盖了,执行source ~/.bashrc之后,连ls、cat这些命令都提示"command not found"。

当时第一反应是完蛋了,但其实有标准自救流程。所有命令都失效的时候,绝对路径依然可用,/usr/bin/ls、/usr/bin/cat这些基础命令都还在。我用完整路径先打开.bashrc文件:

/usr/bin/nano ~/.bashrc

找到出错的那行,把PATH写回安全值:

export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

然后重新source。系统恢复正常。这段经历给了一个非常深刻的教训:修改环境变量前先备份目标文件,用cp ~/.bashrc ~/.bashrc.bak,真出问题还能一键还原。另外,涉及PATH修改时永远用追加方式,比如export PATH=$PATH:xxx,而不是覆盖成只有一个路径。

注意:改PATH类配置前,先备份原文件;关闭SSH密码登录前,先确认公钥能稳定登录。这两条操作都是典型的"改错就失联"高危动作。

5.4 远程断了线怎么办

无头服务器最怕的就是"连不上"三个字。有几次我遇到SSH连接超时,第一反应很慌,但有一套标准的排查顺序。

先看机器是否真的在线。家里路由器后台的DHCP客户端列表能看到所有接入设备,如果那里都看不到笔记本的条目,说明机器断网或掉电了,只能人肉走到机器边上接键鼠检查。如果能ping通说明系统还活着,只是SSH服务可能挂了,可以从局域网内另一台机器上尝试重启ssh服务。

还有一个容易被忽视的保命技能是mDNS。只要装了avahi-daemon,同一局域网里的机器可以直接用主机名访问,不需要记住IP:

ping lobster-box.local ssh lobster@lobster-box.local

Ubuntu默认装了avahi,这功能自动生效。固定IP加mDNS双保险,无头服务器的可达性才算真正稳了。每次断线排查别慌乱,先ping,再试主机名,最后才判断是不是硬件掉了。

6. 安全加固与后续扩展

6.1 最小暴露原则与防火墙

无头服务器的安全原则很简单:不暴露的东西就不需要保护。我用ufw做防火墙,只放行真正需要的端口:

sudo apt install -y ufw sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw allow 3306/tcp sudo ufw enable sudo ufw status verbose
端口用途放行策略
22/tcpSSH远程管理必须放行
80/tcpHTTP Web服务按需放行
443/tcpHTTPS Web服务按需放行
3306/tcpMySQL远程访问内网专用

这四个端口分别对应SSH、HTTP、HTTPS和MySQL。MySQL端口是给内网客户端用的,虽然放行了,但前面数据库账号的IP段限制依然生效,等于双重保险。另外建议开启自动安全更新,避免老机器长期不盯而错过关键漏洞补丁。Ubuntu自带unattended-upgrades:

sudo apt install -y unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgrades

然后在配置里把自动重启容忍改小一点,服务器可以允许有个短暂重启窗口来应用内核更新。

6.2 让服务器断电自恢复

实体服务器最关心的就是停电问题。除了笔记本电池这个天然优势外,我再补充两个层面的设置。

BIOS层:大部分笔记本BIOS里都有"AC Power Recovery"或"Restore on AC Power Loss"选项,设成Power On。这样市电恢复后,笔记本会自动开机,不需要人跑过去按电源键。系统层:所有关键服务都用systemd启用了开机自启,重启后自动拉起来。配合笔记本电池策略,一台无人值守服务器的完整存活链路就打通了:断电时电池续命并正常关机,来电后自动开机,开机后自动拉起所有服务,SSH重新可连。

6.3 这台无头服务器现在还能干什么

配置到这里,这台原本吃灰的笔记本已经是一台完全可用的内网服务器了。目前它在我这边的日常任务清单包括:跑公司项目的CI本地提交检查、承担团队内网共享的Git仓库、跑几个定时数据抓取和备份脚本、用Docker托管一些轻量内部服务。8GB内存在这台机器上跑这些负载依然游刃有余。

后续还想折腾的方向也挺多。硬盘空间允许的话可以搭一个内网NAS;如果以后需要更大的算力,有合适的硬件的话,可以试着在这台无头服务器上跑一些轻量级的大模型推理,或者装一个Claude Code这种命令行形态的AI编程助手来帮忙写脚本。无头服务器的乐趣就在这里,它像一个永远在线、随叫随到的数字工位,你想给它安排什么活,只要配置到位,它就能一直干下去。

我在实际操作中最深的一个体会是,无头服务器这东西,看着像是"把电脑扔角落里不管",其实每一条配置背后都是在为"无人值守"四个字兜底。从关闭睡眠状态,到设置静态IP,再到放行防火墙端口,每一步都是提前把未来可能发生的故障堵住。最后再分享一个小技巧:新机器刚配置完,别急着直接丢进角落,先在本地网络里连续跑两三天,每天定时巡检一下温度、负载和磁盘空间,确认一切稳定了再合上盖子让它正式上岗。这样一台旧笔记本,又能体面地再多干好几年。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询