autossh 开机自启全攻略:systemd、rc.local、crontab 三种方案详解

发布时间:2026/10/13 1:35:13
autossh 开机自启全攻略:systemd、rc.local、crontab 三种方案详解 1. 为什么需要 autossh 开机自启这件事做运维或者后端开发的朋友手里大概率不止一台机器。本地开发机、测试环境、线上服务器、家里的树莓派、公司机房的备份节点这些机器之间经常需要保持一条稳定的 SSH 隧道用来做端口转发、内网穿透、远程调试或者数据同步。问题在于SSH 连接本身并不像很多人想象得那么皮实。网络抖动、路由器重启、运营商换 IP、对端机器休眠任何一个环节出问题那条隧道就断了而且断了之后它不会自己回来。我最早的做法是写个 while 循环每隔几秒 ping 一下不通就重连。这个土办法能用但坑特别多ping 通不代表 SSH 通SSH 进程可能僵死但端口还占着重连的时候还会因为 known_hosts 或者残留 socket 文件报错。后来接触到 autossh才算真正把这件事做干净了。autossh 本质上是一个 SSH 的包装器它会启动一个 SSH 进程同时开一条监控通道一旦发现连接异常就自动拉起新的 SSH 进程配合ServerAliveInterval和ServerAliveCountMax这两个 SSH 原生参数基本可以做到断了就重连重连还失败就再重连。但光有 autossh 还不够。机器一重启autossh 进程就没了隧道自然也不会自己起来。所以真正完整的方案是autossh 开机自启。这篇文章就是围绕这个组合展开的我会把 systemd 服务、rc.local、crontab 这三种主流自启方式都拆开讲清楚包括它们各自的适用场景、参数怎么写、坑在哪里。不管你是刚接触 Linux 服务管理的新手还是想把手头那套脚本规范化一下的老手应该都能从里面找到能直接抄的东西。需要先说明一点autossh 解决的是连接保持的问题开机自启解决的是进程拉起的问题这两件事必须分开理解。很多人配了半天发现重启后还是不通往往不是 autossh 没配好而是自启那一层根本没生效或者生效了但环境变量不对。下面我会一层一层拆。2. autossh 与开机自启的整体设计思路2.1 autossh 到底做了什么为什么不能只用 ssh先把 autossh 的工作原理说清楚不然后面配参数就是瞎配。autossh 启动的时候会做这么几件事它先在一个指定端口上起一个监听默认是 0表示让系统随机分配然后启动真正的 ssh 进程并通过-L或者环境变量告诉 ssh 把这条监控通道也转发出去。ssh 进程会定期往这条通道里发心跳autossh 在另一头等着收。如果连续一段时间收不到心跳autossh 就判定连接挂了杀掉旧的 ssh 进程重新起一个。这里有个关键点autossh 判断连接是否存活靠的是它自己那条监控通道而不是 ssh 的退出码。因为 ssh 在很多情况下进程还在、但连接已经死了退出码根本不会触发。这就是为什么单纯用ssh -o ServerAliveInterval不够——它只能让 ssh 自己发现连接死了然后退出但退出之后谁来拉起它没人。autossh 补的就是这个谁来拉起的角色。那为什么还要配ServerAliveInterval呢因为 autossh 的监控通道本身也是走 ssh 的如果 ssh 底层 TCP 连接已经僵死监控通道也发不出去autossh 要等很久才能发现。加上ServerAliveInterval30和ServerAliveCountMax3意思是 ssh 每 30 秒发一次心跳连续 3 次没回应也就是 90 秒就主动断开这样 autossh 能更快感知到并重连。这两个参数和 autossh 是配合关系不是二选一。2.2 开机自启的三种主流方案怎么选Linux 上做开机自启常见的有三条路systemd service、rc.local、crontab 的reboot。它们不是随便选一个就行得看你的系统环境和需求。systemd 是现在绝大多数发行版的默认 init 系统优势非常明显它能管理进程的生命周期autossh 挂了 systemd 可以再拉起来相当于双重保险能设置依赖关系比如等网络就绪再启动能统一管理日志journalctl 直接看还能设置重启策略。缺点是配置文件格式稍微有点学习成本而且不同发行版的 systemd 版本差异偶尔会带来小坑。rc.local 是老派做法本质上是系统启动最后阶段执行的一个脚本。它的优点是简单直观写几行 shell 就行适合快速验证。缺点是它不管理进程autossh 挂了它不管而且现在很多发行版默认不再启用 rc.local需要手动创建和赋权另外它执行时机比较早网络可能还没完全就绪容易启动失败。crontab 的reboot是个折中方案写一行就行执行时机比 rc.local 晚一些网络一般已经就绪。缺点是 cron 的环境变量非常干净PATH 往往只有/usr/bin:/binautossh 如果装在/usr/local/bin就会找不到而且 cron 不管理进程也不方便看日志。我的建议是只要系统有 systemd就优先用 systemd这是最规范、最省心的做法。rc.local 和 crontab 留作备用或者在容器、精简系统里用。下面三节我会分别给出完整配置。2.3 方案选型对比表为了让你一眼看清差异我把三种方案的关键维度整理成表维度systemd servicerc.localcrontab reboot进程管理支持可自动重启不支持不支持日志查看journalctl 统一管理需手动重定向需手动重定向网络依赖可配置 After/Wants执行较早易失败执行较晚较稳环境变量可显式声明继承系统环境极简需全路径配置复杂度中等低低适用场景生产环境首选快速验证/老系统精简系统/临时方案这张表不是让你死记而是帮你建立判断依据。比如你在一个没有 systemd 的 Alpine 容器里那就只能走 rc.local 或者 cron如果你在 Ubuntu 22.04 上还用 rc.local 就有点逆潮流了。3. 核心细节解析与实操要点3.1 autossh 的关键参数逐个拆autossh 的参数不多但每一个都有讲究。我把最常用的几个列出来配合实际命令解释。-M参数指定监控端口。写法是-M 0表示让系统自动分配或者-M 20000指定固定端口。用 0 的好处是不会和别的服务冲突坏处是排查问题时不知道它用了哪个端口。我一般生产环境用固定端口比如-M 0其实也行因为 autossh 会把实际端口通过环境变量传给 ssh不太需要手动干预。但如果你同时跑多个 autossh 实例固定端口就要注意别撞车。-N表示不执行远程命令只做端口转发。这个几乎必加因为我们的目的就是保持隧道不需要开 shell。-o后面跟 ssh 的原生选项最常用的就是ServerAliveInterval和ServerAliveCountMax还有ExitOnForwardFailureyes。最后这个特别重要它的意思是如果端口转发建立失败ssh 直接退出这样 autossh 就能感知到并重试。如果不加这个ssh 可能在转发失败的情况下还傻乎乎地待着autossh 以为一切正常。-i指定私钥文件。做自启的时候绝对不能依赖交互式密码输入必须用密钥认证而且私钥最好不设密码或者用 ssh-agent但自启场景下 agent 不一定可用。所以生成密钥的时候直接回车留空密码这是自启场景的标准做法。-p指定远程端口-l指定登录用户-L或-R做本地/远程转发。这些和普通 ssh 一样不展开。一个典型的完整命令长这样autossh -M 0 -N \ -o ServerAliveInterval 30 \ -o ServerAliveCountMax 3 \ -o ExitOnForwardFailureyes \ -o StrictHostKeyCheckingno \ -i /home/youruser/.ssh/id_rsa_tunnel \ -L 8080:127.0.0.1:80 \ userremote-host -p 22这里StrictHostKeyCheckingno是为了避免首次连接时因为 known_hosts 没有记录而卡在交互确认上。自启场景下这个交互是致命的会导致服务一直起不来。当然从安全角度更稳妥的做法是提前把对端 host key 写进 known_hosts但no在内部可信网络里用得多。3.2 密钥与权限自启失败的隐形杀手我见过太多人 autossh 命令手动跑没问题一做成服务就失败最后发现是密钥权限问题。ssh 对私钥权限极其敏感如果私钥文件权限是 644 或者所属用户不对ssh 会直接拒绝使用报 Permissions 0644 for xxx are too open。手动跑的时候你可能用的是当前用户权限刚好对做成 systemd 服务如果指定了别的用户权限就不对了。标准做法是chmod 700 ~/.ssh chmod 600 ~/.ssh/id_rsa_tunnel chmod 644 ~/.ssh/id_rsa_tunnel.pub chmod 600 ~/.ssh/known_hosts另外如果 systemd 服务里用Useryouruser指定了运行用户那这个用户必须对私钥有读权限且私钥的属主必须是这个用户。跨用户读私钥是行不通的ssh 会拒绝。还有一个容易忽略的点known_hosts文件。如果服务运行用户的家目录下没有这个文件或者文件里没有对端的记录配合StrictHostKeyCheckingno可以绕过但会每次连接都重新确认。更规范的做法是提前用该用户手动 ssh 一次把 host key 写进去。3.3 网络就绪判断别让服务跑在网络前面开机自启最容易踩的坑就是服务起来了但网络还没通。systemd 默认的服务启动顺序里网络可能还在初始化autossh 一连就失败然后如果没配重启策略它就彻底躺平了。解决办法有两层。第一层是在 service 文件里声明网络依赖Afternetwork-online.target Wantsnetwork-online.target注意是network-online.target而不是network.target。后者只表示网络管理服务启动了不表示网络真的通了前者才表示网络已经配置完成、可以对外通信。但network-online.target本身也需要系统里有对应的 wait-online 服务才会真正等待有些精简系统里没有这时候就要靠第二层。第二层是给 autossh 加-o ConnectTimeout10让它在连接超时后快速失败退出然后靠 systemd 的Restartalways和RestartSec10不断重试。这样即使第一次网络没通10 秒后重试一次几次之后网络就绪了自然就连上了。这个组合比单纯依赖网络依赖更可靠。3.4 日志与排查出问题先看哪里autossh 本身日志不多主要靠 ssh 的-v参数输出。调试阶段可以在命令里加-vsystemd 服务里就能通过journalctl -u 服务名 -f实时看到连接过程。但生产环境不建议长期开-v日志量太大。systemd 服务的日志查看命令journalctl -u autossh-tunnel.service -f journalctl -u autossh-tunnel.service --since 10 min ago如果是 rc.local 或 cron 启动的就得自己在命令里重定向输出比如 /var/log/autossh.log 21否则出错了你什么都看不到。排查顺序我一般是这样先看进程在不在ps aux | grep autossh再看日志有没有报错再看网络通不通ping对端、telnet对端端口最后看密钥和 known_hosts。这个顺序能覆盖 90% 的问题。4. 三种开机自启方案的完整实操4.1 systemd 方案生产环境首选先确认 autossh 装好了路径记下来which autossh # 通常输出 /usr/bin/autossh 或 /usr/local/bin/autossh然后创建服务文件/etc/systemd/system/autossh-tunnel.service[Unit] DescriptionAutoSSH Tunnel Service Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple Useryouruser Groupyouruser EnvironmentAUTOSSH_GATETIME0 EnvironmentAUTOSSH_LOGLEVEL7 ExecStart/usr/bin/autossh -M 0 -N \ -o ServerAliveInterval 30 \ -o ServerAliveCountMax 3 \ -o ExitOnForwardFailureyes \ -o StrictHostKeyCheckingno \ -o ConnectTimeout10 \ -i /home/youruser/.ssh/id_rsa_tunnel \ -L 8080:127.0.0.1:80 \ userremote-host -p 22 Restartalways RestartSec10 [Install] WantedBymulti-user.target几个关键点解释一下。AUTOSSH_GATETIME0是让 autossh 在启动后立即开始监控而不是等一段时间。默认情况下 autossh 有个启动宽限期如果 ssh 在宽限期内退出autossh 会认为这是正常的启动失败而不重试。设成 0 可以避免这个行为让重试更积极。AUTOSSH_LOGLEVEL7是日志级别调试阶段用稳定后可以去掉。Restartalways和RestartSec10是 systemd 层面的兜底即使 autossh 进程本身挂了比如被 OOM 杀掉systemd 也会在 10 秒后重新拉起它。这就形成了 autossh 管 ssh、systemd 管 autossh 的双层保险。Useryouruser指定运行用户这个用户必须对私钥有权限。如果你的私钥在 root 家目录下那要么改成 root 运行不推荐要么把私钥复制一份给 youruser 并改好权限。写完服务文件后执行sudo systemctl daemon-reload sudo systemctl enable autossh-tunnel.service sudo systemctl start autossh-tunnel.service sudo systemctl status autossh-tunnel.serviceenable是设置开机自启start是立即启动。status看状态如果显示active (running)就说明起来了。然后可以reboot重启验证一下重启后直接systemctl status看是否自动运行。注意如果服务状态是activating (auto-restart)反复循环说明 autossh 一直在失败重启。这时候用journalctl -u autossh-tunnel.service -n 50看最近 50 行日志基本能定位到原因最常见的是密钥权限、网络不通、或者对端 host key 问题。4.2 rc.local 方案老系统与快速验证先确认系统有没有 rc.local。现在很多发行版默认没有这个文件需要手动创建sudo touch /etc/rc.local sudo chmod x /etc/rc.local然后编辑内容#!/bin/bash # rc.local /usr/bin/autossh -M 0 -N \ -o ServerAliveInterval 30 \ -o ServerAliveCountMax 3 \ -o ExitOnForwardFailureyes \ -o StrictHostKeyCheckingno \ -i /home/youruser/.ssh/id_rsa_tunnel \ -L 8080:127.0.0.1:80 \ userremote-host -p 22 /var/log/autossh.log 21 exit 0注意最后的是让 autossh 在后台运行否则 rc.local 会一直卡在那里不返回导致系统启动流程被阻塞。exit 0是 rc.local 的标准结尾表示成功退出。还要确认 rc-local 服务是启用的sudo systemctl enable rc-local sudo systemctl start rc-local有些系统里这个服务叫rc-local.service有些叫rc.local.service用systemctl list-unit-files | grep rc查一下。rc.local 的坑在于执行时机早网络可能没就绪。如果发现重启后隧道没起来可以在命令前加个sleep 30等网络或者干脆改用 systemd。另外 rc.local 里的环境变量是继承系统的PATH 一般够用但保险起见还是写全路径。4.3 crontab 方案一行搞定的轻量做法用运行 autossh 的那个用户执行crontab -e添加一行reboot sleep 30 /usr/bin/autossh -M 0 -N -o ServerAliveInterval 30 -o ServerAliveCountMax 3 -o ExitOnForwardFailureyes -o StrictHostKeyCheckingno -i /home/youruser/.ssh/id_rsa_tunnel -L 8080:127.0.0.1:80 userremote-host -p 22 /home/youruser/autossh.log 21reboot表示开机时执行一次sleep 30是等网络就绪。cron 的环境变量极简所以 autossh 必须写全路径私钥也必须写全路径日志重定向也要写全路径因为 cron 的工作目录不确定。cron 方案最大的问题是排查困难。如果没起来先看/var/log/syslog或者/var/log/cron里有没有 cron 执行记录再看你重定向的那个日志文件。另外 cron 的reboot在某些系统上执行时机不稳定可能早于网络所以sleep是必要的。4.4 三种方案的重启验证流程不管用哪种方案配完都要做一次完整的重启验证不能只看start成功就完事。验证流程先手动stop掉当前运行的 autossh确认端口释放。执行sudo reboot重启机器。等机器起来后先ps aux | grep autossh看进程在不在。再看ss -tlnp | grep 8080看转发端口有没有监听。最后从本地实际访问一下转发过去的服务确认隧道真的通了。这五步走完才算真正验证通过。我见过有人只做了前三步就以为成功了结果端口监听了但转发不通因为ExitOnForwardFailure没配ssh 假装在工作。5. 常见问题与排查技巧实录5.1 服务反复重启日志报密钥权限错误这是最高频的问题。日志里会出现Permissions 0644 for /home/youruser/.ssh/id_rsa_tunnel are too open或者Load key ... bad permissions。原因就是私钥权限太开放ssh 拒绝使用。解决chmod 600私钥chmod 700所在目录确认属主是运行用户。如果是 systemd 服务还要确认User指定的用户和私钥属主一致。5.2 重启后进程在但隧道不通进程在说明 autossh 起来了但隧道不通可能是转发没建立成功。检查ExitOnForwardFailureyes有没有加没加的话 ssh 可能在转发失败的情况下还保持运行。另外检查对端端口是不是被占用本地转发端口是不是和别的服务冲突。还有一种情况是ServerAliveInterval没配连接僵死后 autossh 要很久才发现。加上这两个参数能显著改善。5.3 首次连接卡在 host key 确认日志里出现The authenticity of host ... cant be established然后服务就卡住了。这是因为 known_hosts 里没有对端记录ssh 在等交互确认。自启场景下没有终端可以确认所以要么提前手动 ssh 一次写入 known_hosts要么加StrictHostKeyCheckingno。5.4 cron 启动失败但手动执行正常典型的环境变量问题。cron 的 PATH 通常只有/usr/bin:/bin如果 autossh 装在/usr/local/bincron 就找不到。解决命令里写全路径或者在 crontab 开头加PATH/usr/local/bin:/usr/bin:/bin。另外 cron 不加载用户的.bashrc所以任何依赖 shell 配置的东西都不会生效。5.5 问题速查表现象可能原因排查命令解决方向服务反复重启密钥权限/网络不通journalctl -u 服务名改权限/加网络依赖进程在但隧道不通转发失败未退出ss -tlnp加 ExitOnForwardFailure卡在 host key 确认known_hosts 缺失看日志预写 known_hosts 或 nocron 启动失败PATH 不全看 cron 日志写全路径连接频繁断开心跳参数缺失看日志频率加 ServerAlive 参数重启后完全没进程自启未 enablesystemctl is-enabled执行 enable5.6 几个我踩过的坑第一个坑是AUTOSSH_GATETIME。默认值是 30 秒意思是 autossh 启动后 30 秒内如果 ssh 退出了它认为这是启动失败不会重试。如果你的网络刚好在这个窗口内没就绪autossh 就直接放弃了。设成 0 可以避免但要注意设成 0 后如果 ssh 命令本身写错了autossh 会疯狂重试刷日志。所以调试阶段先手动跑通命令再设 0。第二个坑是多个 autossh 实例共用监控端口。如果你用-M 20000固定端口又跑了两个实例第二个会启动失败。用-M 0让系统分配就没这个问题。第三个坑是 systemd 的Type。默认是simple对 autossh 来说够用。但如果你写成forkingautossh 不会 fork服务会一直卡在 activating。这个坑很隐蔽因为forking对很多守护进程是标准写法。第四个坑是私钥带密码。自启场景下没有终端输入密码带密码的私钥会导致 ssh 卡住等输入。要么用无密码私钥要么配 ssh-agent 并确保 agent 在服务启动前可用这个在 systemd 里很麻烦不推荐。6. 进阶优化与长期维护建议6.1 用 ssh config 简化命令命令太长不好维护可以把连接参数写进~/.ssh/configHost tunnel-target HostName remote-host Port 22 User user IdentityFile /home/youruser/.ssh/id_rsa_tunnel ServerAliveInterval 30 ServerAliveCountMax 3 ExitOnForwardFailure yes StrictHostKeyChecking no ConnectTimeout 10然后 autossh 命令就能简化成autossh -M 0 -N -L 8080:127.0.0.1:80 tunnel-target这样 service 文件干净很多改参数也只改一处。注意IdentityFile要写绝对路径因为服务运行时的家目录可能和交互式登录不同。6.2 日志轮转与监控长期运行的服务日志会越来越大尤其是开了-v的时候。systemd 服务可以用 journald 的配置做轮转rc.local 和 cron 方案就得配 logrotate。简单做法是在/etc/logrotate.d/下加一个配置/home/youruser/autossh.log { weekly rotate 4 compress missingok notifempty }监控方面可以写个简单的脚本定期检查端口是否监听不通就告警。或者更直接一点用 systemd 的OnFailure触发一个通知脚本。这些属于锦上添花核心还是把 autossh 和自启配稳。6.3 多隧道场景的组织方式如果你需要维护多条隧道不要把所有转发都塞进一个 autossh 实例。一个实例挂了所有隧道都断而且日志混在一起不好排查。更好的做法是每条隧道一个 service 文件命名上区分开比如autossh-tunnel-a.service、autossh-tunnel-b.service。这样互不影响单独重启也方便。如果隧道数量很多可以考虑用模板服务systemd template unit文件名写成autossh-tunnel.service用实例名来启动不同实例配置里用%i引用实例名。这个稍微进阶但确实能减少重复配置。6.4 定期验证自启有效性的习惯配置不是一劳永逸的。系统升级、内核更新、网络配置变更都可能影响自启。我的习惯是每个月至少主动重启一次关键节点验证隧道能自动恢复。另外在变更网络配置或者防火墙规则后也要重新验证一遍。这个习惯帮我提前发现过好几次问题避免了真正需要隧道时才发现它起不来。说到底autossh 加开机自启这套东西配置本身不难难的是把每个细节都考虑到密钥权限、网络依赖、心跳参数、日志排查。这些细节单看都是小事但任何一个出问题都会让整套方案失效。我个人的经验是第一次配的时候多花半小时把日志和重启验证做扎实后面能省下无数个半夜爬起来查隧道的夜晚。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询