龙虾尝鲜记这个系列写到第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.targetmask操作比disable更彻底等于在systemd的目标列表里把睡眠目标指向空设备任何服务想触发休眠都会失败。同时还要修改logind.conf让合盖动作不再挂起sudo sed -i s/#HandleLidSwitchsuspend/HandleLidSwitchignore/ /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如果这台笔记本要连WiFinetplan同样支持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 sshsshd -t这一步很多人不做但它能在重载前先检查语法错误配置写坏了不至于当场断连。凡是改网络、改SSH这类改完可能断连的配置都先做干跑测试这是生产环境养成的习惯。3.2 公钥登录配置密码登录虽然方便但暴力破解的威胁始终存在而且每次登录敲密码也比密钥麻烦得多。我在客户端电脑上生成一对SSH密钥然后把公钥推到服务器上ssh-keygen -t ed25519 -C lobster-headless ssh-copy-id lobster192.168.1.100现在主流推荐ed25519而不是RSA它的密钥长度更短安全强度相当而且全程在本地客户端生成私钥不会离开本地机器。ssh-copy-id会自动把公钥追加到服务器上~/.ssh/authorized_keys里并设置好权限。做完这个先验证一下能不能免密登录ssh lobster192.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] DescriptionMy Backup Job [Service] ExecStart/usr/local/bin/backup_job.sh Restartalways Userlobster [Install] WantedBymulti-user.target第三是给自己写了一套一台机器管全家的辅助脚本。比如想一次性看到CPU、内存、磁盘和运行中服务用一行组合命令alias mzecho CPU ; top -bn1 | head -5; echo MEM ; free -h; echo DISK ; df -h; echo ACTIVE SERVICES ; systemctl list-units --typeservice --staterunning --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 lobsterexample.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而不是aptUbuntu的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 -vnvm的好处非常具体不同项目依赖不同版本的Nodenvm install和nvm use就能秒切卸载也干净不会污染系统级的/usr目录。npm registry这一块在家庭或内网环境里直接用默认官方源基本没压力如果拉包慢按自己实际的网络条件配置合适的registry地址即可。反正端口默认对着外网只要网络畅通问题都不大。4.3 MySQL8.0的坑与基础配置数据库选了MySQL因为团队已有的项目都跑MySQL兼容性最省心。Ubuntu 22.04官方源里自带MySQL 8.0一条命令装好sudo apt install -y mysql-server sudo systemctl enable --now mysqlMySQL 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 app192.168.1.% IDENTIFIED WITH mysql_native_password BY StrongPass2024!; GRANT ALL PRIVILEGES ON lobster_app.* TO app192.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按CtrlX启动进入单用户bash。因为系统以只读方式挂载需要先重新挂载根分区mount -o remount,rw / passwd lobster然后exit或按CtrlAltDelete重启密码就重置成功了。这套操作在有键盘屏幕的笔记本上非常顺手如果是纯远程无头环境就麻烦多了。这也侧面说明用带键盘屏幕的笔记本做无头服务器有多合理的理由——救急就是成本。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 modeset0更新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同一局域网里的机器可以直接用主机名访问不需要记住IPping lobster-box.local ssh lobsterlobster-box.localUbuntu默认装了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-upgradessudo apt install -y unattended-upgrades sudo dpkg-reconfigure --prioritylow 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再到放行防火墙端口每一步都是提前把未来可能发生的故障堵住。最后再分享一个小技巧新机器刚配置完别急着直接丢进角落先在本地网络里连续跑两三天每天定时巡检一下温度、负载和磁盘空间确认一切稳定了再合上盖子让它正式上岗。这样一台旧笔记本又能体面地再多干好几年。
