搭建 Debian13 网站服务器全流程指南
categories: 技术 tags: Linux从零搭建一个 Debian13 网站服务器
最近发现家里面开通的移动宽带,在光猫中调整防火墙等级为“低”后,竟然可以从公网直接访问 80/443 端口??
这不拿来搭网站太浪费了,直接开干!
系统选择
对 Linux 比较熟的可以选择像我一样选 Debian,只懂基本命令的可以选 Fedora,喜欢 Snap(真的有吗?)的可以选 Ubuntu-Server,不怕死的选 Arch,系统洁癖选 NixOS。
- Debian 13:稳,但是刚安装好过于毛胚,需要花大量时间配置,配好后爽。
- Fedora:软件包新,功能强大,自带管理页面,但资源占用比 Debian 高一些,需要手动关 SE Linux。
- Ubuntu Server:我不推荐,因为 Snap 要开始污染你的 apt 了,有 nvidia 显卡的可以选,驱动安装方便。
- Archlinux:软件包丰富,现在基本上也不会滚挂了。安装复杂但也没那么难。维护麻烦,要定期更新。
- NixOS:个人认为最适合做服务器的 Linux 系统,软件维护、回退、环境复现都完爆上面几个。但是上手门槛过高,应该会长期处于邪教状态。
我就折衷选了 Debian 13,上班了真没多少时间折腾 NixOS 咯。
系统配置
系统装好后,会有两个用户 root 和你自己创建的用户,这里假设为 admin。刚装好的毛胚连 sudo 都没有,因此要先用 root 用户进行配置。
换源
先清空 sources.list:echo > /etc/apt/sources.list.
然后 vi /etc/apt/sources.list.d/debian13.sources,粘贴下面的内容
# 默认注释了源码仓库,如有需要可自行取消注释
deb http://mirrors.ustc.edu.cn/debian trixie main contrib non-free non-free-firmware
# deb-src http://mirrors.ustc.edu.cn/debian trixie main contrib non-free non-free-firmware
deb http://mirrors.ustc.edu.cn/debian trixie-updates main contrib non-free non-free-firmware
# deb-src http://mirrors.ustc.edu.cn/debian trixie-updates main contrib non-free non-free-firmware
# backports 软件源,请按需启用
# deb http://mirrors.ustc.edu.cn/debian trixie-backports main contrib non-free non-free-firmware
# deb-src http://mirrors.ustc.edu.cn/debian trixie-backports main contrib non-free non-free-firmware
apt update; apt upgrade -y 升级一下软件包和内核。
安装必备软件
apt install neovim sudo zsh git
visudo 添加 admin 用户的 root 权限。NOPASSWD 参数表示不用输密码就能用 root 权限,新手一定不要添加该参数。
# User privilege specification
root ALL=(ALL:ALL) ALL
admin ALL=(ALL:ALL) NOPASSWD: ALL
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL
Clash
本方案将 clash 作为 systemd 服务进行自启动,配合 Zsh 配置中的 proxy_on、proxy_off函数来进行使用。
下载 clashpremium-nightly-linux-amd64.tar.gz 文件并上传到服务器。使用 root 用户登录到服务器(所有涉及到服务的操作最好都用 root 用户)。
sudo su
mkdir /opt/clash/
mv clashpremium-nightly-linux-amd64.tar.gz /opt/clash/
cd /opt/clash/
tar -zxvf clashpremium-nightly-linux-amd64.tar.gz
chmod +x CrashCore
会解压出一个/opt/clash/CrashCore文件。
wget -O config.yaml "https://0b3....b/lin...k/gVLd?cla...sh=3"
拉取自己的配置文件,放置到 /opt/clash/,此时应该有以下文件:
❯ cd /opt/clash
❯ ls
. .. config.yaml CrashCore
通过命令 /opt/clash/CrashCore -d /opt/clash/ 可以启动。
vim /etc/systemd/system/clash.service vim 一个 systemd service,填入以下内容,如果路径跟我不一样要记得更改。
[Unit]
Description=Clash Meta Core Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/opt/clash
ExecStart=/opt/clash/CrashCore -d /opt/clash
Restart=on-failure
RestartSec=5s
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
然后添加自启动:
systemctl daemon-reload
systemctl enable --now clash
通过 systemctl status clash 可以查看服务状态。
❯ systemctl status clash
● clash.service - Clash Meta Core Service
Loaded: loaded (/etc/systemd/system/clash.service; enabled; preset: enabled)
Active: active (running) since Tue 2025-10-14 08:27:39 CST; 2h 40min ago
Invocation: fc105af99f394b7fadb5d0a5d01fa3da
Main PID: 897 (CrashCore)
Tasks: 8 (limit: 18716)
Memory: 31.8M (peak: 32.2M)
CPU: 3.984s
CGroup: /system.slice/clash.service
└─897 /opt/clash/CrashCore -d /opt/clash
Warning: some journal files were not opened due to insufficient permissions.
通过 proxy_on、proxy_off 可以在终端中启用和关闭。
Zsh
我不用 oh-my-zsh 这类插件,而是自行配置。首先使用普通用户登入(例如 admin),拉取功能插件,记得先 proxy_on 解决网络连接问题。
# 自动补全
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions ~/.zsh/plugins/zsh-autosuggestions
# 语法高亮
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting ~/.zsh/plugins/zsh-syntax-highlighting
# 这个插件我也没弄明白是干嘛的,可选。。。。
git clone --depth=1 https://github.com/zsh-users/zsh-completions ~/.zsh/plugins/zsh-completions
# p10k 主题
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/.zsh/plugins/powerlevel10k
将 zsh 的插件统一放置在 ~/.zsh/plugins/ 下,这一步可以顺便把访问终端的字体更新一下,推荐使用 nerd-fonts-hack 字体作为终端字体。
环境变量存放位置为 ~/.zprofile,配置文件在 ~/.zshrc,主要编写 ~/. zshrc 文件
将以下代码加入 .zshrc 以使用插件
### 常用命令别名 {{{
alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'
alias ls='ls -a --color=auto'
alias ll='ls -al --color=auto'
alias grep='grep --color=auto'
# alias nvim="nvim -u ~/.config/nvim/init-plugin.lua"
# }}}
### 个性化功能配置 {{{
# 容器相关
pm_stop_all() { podman stop $(podman ps -q); }
pm_list_ct() { podman ps -a; }
pm_list_net() { podman network ls; }
pm_list_pod() { podman pod ls; }
pm_clean() { echo "y" | podman system prune -a --volumes; }
# 更新软件
update() { sudo apt update; sudo apt full-upgrade -y; }
# 清理垃圾
clean() { sudo apt clean; sudo apt autoremove -y; pm_clean; }
# 双击 ESC 在命令前插入 sudo
sudo-command-line() {
[[ -z $BUFFER ]] && zle up-history
[[ $BUFFER != sudo\ * ]] && BUFFER="sudo $BUFFER"
zle end-of-line #光标移动到行末
}
zle -N sudo-command-line
bindkey "\e\e" sudo-command-line
# }}}
### 终端代理 {{{
hostip="127.0.0.1"
# hostip=$(cat /etc/resolv.conf |grep -oP '(?<=nameserver\ ).*')
proxy_on() {
proxy_status="on"
export https_proxy="http://${hostip}:7890"
export http_proxy="http://${hostip}:7890"
export all_proxy="socks5://${hostip}:7890"
echo -e "终端代理已开启。"
}
proxy_off(){
unset http_proxy https_proxy all_proxy
echo -e "终端代理已关闭。"
proxy_status="off"
}
# }}}
### 历史纪录相关配置 {{{
# 历史文件路径
export HISTFILE="$HOME/.zsh_history"
# 历史纪录条目数量
export HISTSIZE=200
# 注销后保存的历史纪录条目数量
export SAVEHIST=200
# 以附加的方式写入历史纪录
setopt INC_APPEND_HISTORY
# 如果连续输入的命令相同,历史纪录中只保留一个
setopt HIST_IGNORE_DUPS
# 为历史纪录中的命令添加时间戳
setopt EXTENDED_HISTORY
# 启用 cd 命令的历史纪录,cd -[TAB]进入历史路径
setopt AUTO_PUSHD
# 相同的历史路径只保留一个
setopt PUSHD_IGNORE_DUPS
# 转换时间戳并打印历史命令
hist() {
# 获取参数:不传则显示全部,传数字则显示最近 N 条
local lines="${1:-0}"
awk -F': |:0;' '
/^: [0-9]+:[0-9]*;/ {
printf "%s | %s\n", strftime("%Y-%m-%d %H:%M:%S", $2), $3
}
' $HOME/.zsh_history | if [ "$lines" -gt 0 ] 2>/dev/null; then
tail -n "$lines"
else
cat
fi
}
# }}}
### 杂项 {{{
# 编辑器
export EDITOR=vim
export VISUAL=vim
# 禁用 core dumps
limit coredumpsize 0
# 以下字符视为单词的一部分
WORDCHARS='*?_-[]~=&;!#$%^(){}<>'
# }}}
p10k 在首次进入时需要进行配置,按照提示进行即可,配置之后,会在 .zshrc 行首和行尾分别添加
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi
以及
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
需要把上边 if 那三句代码移动到 4 个 source 的下面,否则每次启动终端都会报错。
Neovim(可选)
虽然说这台机器只是一个网页服务器,但有一个好看好用的瑞士军刀也不是不行。
首先确保安装好 neovim。
容器
docker hub 现在几乎无法通过软路由之外的方式解决网络问题,所以使用 podman 进行代替。
sudo apt install podman podman-compose
在 pull/up -d 之前先用 proxy_on 开启代理即可。
podman 相比 docker 缺少了守护进程,这导致 podman 跑起来的容器在终端用户退出之后被 systemd 清理机制杀掉。因此需要运行 podman 容器的用户来执行
loginctl enable-linger $USER
启用后,用户级 systemd 实例会在系统启动时即常驻,即使用户注销,容器进程也不会被清理。这也比较符合服务器的基本使用习惯。
DDNS 与反向代理
原本想用 lucky 来进行 DDNS 和反向代理,毕竟这个一站式管理工具确实很方便。但仔细想了想,还是拥抱开源算了,毕竟有 AI 之后写配置没有以前麻烦。
ddns-go
下载地址,选择 ddns-go_XXXX_linux_x86_64.tar.gz
将文件上传到 /opt/ddns-go,这个步骤就不细说了。
登录到服务器系统,切换到 root 用户,cd /opt/clash/,解压缩 tar- zxvf ddns-go_XXXX_linux_x86_64.tar.gz 后出现以下程序,该程序默认拥有可执行权限。
❯ ls
. .. ddns-go
安装 ddns-go 服务:/opt/ddns-go/ddns-go -s install,最好使用绝对路径。
在网页中访问 ip:9876 就可以进行 ddns-go 配置页面。
顺带说一下 IPv6 解析问题,推荐关闭有状态 DHCPv6,仅采用 SLAAC(无状态 DHCPv6)模式,安全性高一些,实在需要固定后缀的话,通过 EUI-64 方式进行固定。
SLAAC + EUI-64 + 运营商动态前缀,没有被扫到的可能性。
Nginx 反向代理
现在反向代理门槛比以前低很多了,搭配 certbot,连 SSL 证书都不需要操心。首先登录到 root 用户。
apt install certbot nginx python3-certbot-nginx
创建反向代理配置,这里用 ddns-go 做示例,ddns-go 默认禁止从公网访问,配置反向代理后,实际访问者是本机,就可以从公网访问了。
cd /etc/nginx/sites-available 然后创建一个 ddns-go 文件,输入以下内容:
❯ cat sites-available/ddns-go
# 默认 server,强制跳 HTTPS
server {
listen 80;
listen [::]:80;
server_name youdomain;
return 301 https://$host$request_uri;
}
# HTTPS 反代
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name youdomain;
# SSL 证书路径(certbot 自动生成)
ssl_certificate /etc/letsencrypt/live/youdomain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/youdomain/privkey.pem;
# 安全强化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# 反代到 ddns-go
location / {
proxy_pass http://127.0.0.1:9876;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
如果使用 certbot 生成证书的话,就不用改这两行
# SSL 证书路径(certbot 自动生成)
ssl_certificate /etc/letsencrypt/live/youdomain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/youdomain/privkey.pem;
如果公网是 IPv6,则一定要添加 IPv6 监听:
listen [::]:80;
listen [::]:443 ssl http2;
当然你也可以使用自己的证书。
那么如何使用 certbot 自动生成证书呢?
首先确保自己安装了 certbot python3-certbot-nginx,打开代理 proxy_on,使用
sudo certbot certonly --nginx -d "yourdomain"
输入你的邮箱,然后选择 Y、N,回车就完事了,失败的话就是网络有问题,注意修复。
防火墙
虽然之前有说 IPv6 很安全,但一旦别人得知你的域名,那么不需要扫描也可以得到你的 IPv6 地址,因此防火墙还是要做好的。
使用普通用户登入,sudo apt install ufw 安装防火墙
❯ sudo apt install ufw # 安装(通常已预装)
将要安装:
ufw
......
Creating config file /etc/ufw/after6.rules with new version
Created symlink '/etc/systemd/system/multi-user.target.wants/ufw.service' → '/usr/lib/systemd/system/ufw.service'.
正在处理用于 man-db (2.13.1-1) 的触发器 ...
sudo ufw status verbose # 查看状态
安装后默认不会启用,我们先配置再启用,先把最重要的几个启用了,免得登陆不上。
首先是出入站行为设置:
# 兜底规则,拒绝入站,允许出站
sudo ufw default deny incoming
sudo ufw default allow outgoing
然后开放几个重要的应用层。
# 允许 SSH,HTTP,HTTPS
❯
sudo ufw allow ssh
sudo ufw allow 80
sudo ufw allow 443
尤其是 ssh 一定要开放,输入 sudo ufw enable 激活防火墙,选择 y 即可。
❯ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup
可以通过以下命令查看端口开放状态。
❯ sudo ufw status numbered
Status: active
To Action From
-- ------ ----
[ 1] 22/tcp ALLOW IN Anywhere
[ 2] 80 ALLOW IN Anywhere
[ 3] 443 ALLOW IN Anywhere
[ 4] 22/tcp (v6) ALLOW IN Anywhere (v6)
[ 5] 80 (v6) ALLOW IN Anywhere (v6)
[ 6] 443 (v6) ALLOW IN Anywhere (v6)
ufw 适合快速上手的常用操作手册:
- 限制 IP 访问
sudo ufw allow from 192.168.1.100 to any port 22
- 拒绝某个 IP
sudo ufw deny from 203.0.113.5
- 删除规则
sudo ufw delete allow 8080/tcp
# 或按编号删除
sudo ufw status numbered
sudo ufw delete 3
注意,通过编号删除后,剩下的规则编号可能会发生变化,一定要一条一条删。
- 关闭与重置
sudo ufw disable # 关闭防火墙
sudo ufw reset # 清除所有规则(谨慎)
- 规则检查
使用 sudo ufw status verbose 随时检查规则是否生效。
❯ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
80 ALLOW IN Anywhere
443 ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
80 (v6) ALLOW IN Anywhere (v6)
443 (v6) ALLOW IN Anywhere (v6)