前言
Proxmox VE(PVE)提供了两种虚拟化方式:KVM 虚拟机和 LXC 容器(CT)。很多新手上来就全开 VM,但其实大量场景下 LXC 容器更轻量、更高效——它不需要模拟硬件,直接共享宿主机内核,启动瞬间完成,资源开销几乎可以忽略。
然而,LXC 也有它的坑:特权与非特权的选择、挂载直通目录的权限问题、在容器里跑 Docker 时的嵌套注意事项……本文结合我自己的 Homelab 实战,从 CT 模板管理到生产级配置,带你完整走完 LXC 容器的全流程。
1. LXC vs VM:什么时候用哪个?
先上一张直观对比表:
| 特性 | LXC 容器 | KVM 虚拟机 |
|---|
| 内核 | 共享宿主机内核 | 独立内核(可不同 OS) |
| 启动速度 | 秒级 | 分钟级 |
| 内存开销 | 几乎零开销 | 需额外分配内存 |
| 磁盘占用 | MB 级(共享基础模板) | GB 级(完整 OS 镜像) |
| 隔离性 | 中等(共享内核) | 强(完全隔离) |
| 硬件直通 | 大部分不支持 | 支持 GPU/PCIe/USB 直通 |
| Docker 支持 | 需要嵌套或特权模式 | 原生支持 |
我的选择原则:
- 用 LXC ✅:跑 Docker 服务(Home Assistant Core、Nginx、数据库)、DNS 服务器(AdGuard Home)、轻量网络工具、编译构建环境
- 用 VM ✅:需要不同内核的场景(TrueNAS 需要 FreeBSD)、需要 GPU/USB 直通的场景(Jellyfin 硬解、Windows 虚拟机)、对安全隔离要求极高的场景
简单说:能用 LXC 解决的,别开 VM。
2. CT 模板管理
2.1 下载模板
PVE 默认提供了一个模板仓库,但下载速度取决于你的网络环境:
1
2
3
4
5
6
7
8
9
10
11
| # 查看可用模板列表
pveam available
# 按系统过滤(推荐 Ubuntu 24.04 / Debian 12)
pveam available | grep -E "ubuntu-24|debian-12"
# 下载到本地存储(这里以 local 存储为例)
pveam download local ubuntu-24.04-standard_24.04-2_amd64.tar.zst
# 查看已下载的模板
pveam list local
|
2.2 更换国内镜像源(可选)
PVE 默认模板源在国外,如果下载慢,可以配置国内镜像。编辑 /etc/apt/sources.list.d/pve-enterprise.list 注释掉企业源,然后添加:
1
2
| # /etc/apt/sources.list
deb http://mirrors.ustc.edu.cn/proxmox/debian bookworm pve-no-subscription
|
模板下载源在 /usr/share/perl5/PVE/APLInfo.pm 中定义,不建议直接修改,更推荐用代理下载后 pveam download 到本地。
3. 创建 LXC 容器(命令行 vs Web UI)
3.1 Web UI 方式
PVE 管理界面 → 选择节点 → 右上角「Create CT」→ 按提示填写。这是最直观的方式,适合新手。
3.2 命令行方式(可脚本化)
用 pct create 可以全命令行创建,适合批量部署:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| # 定义变量
CT_ID=200
HOSTNAME="docker-host"
TEMPLATE="local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst"
STORAGE="local-lvm"
ROOT_PASSWORD="YourStrongPassword"
IP="192.168.1.200/24"
GW="192.168.1.1"
# 创建容器(非特权模式)
pct create $CT_ID $TEMPLATE \
--hostname $HOSTNAME \
--storage $STORAGE \
--rootfs $STORAGE:8 \
--cores 2 \
--memory 2048 \
--swap 512 \
--net0 name=eth0,bridge=vmbr0,ip=$IP,gw=$GW \
--password $ROOT_PASSWORD \
--unprivileged 1 \
--features nesting=1
# 启动容器
pct start $CT_ID
# 进入容器
pct enter $CT_ID
|
⚠️ 注意:pct enter 直接进入容器根 shell,不需要密码,适合排错。生产环境建议用 SSH 登录。
4. 特权与非特权容器:核心必知
这是 LXC 最容踩坑的地方,一定要理解清楚。
4.1 区别
| 特性 | 非特权 (Unprivileged) | 特权 (Privileged) |
|---|
| UID 映射 | 容器内 UID 0 映射到宿主机普通 UID(如 100000) | 容器内 UID 0 = 宿主机 UID 0 |
| 安全性 | ✅ 极高,容器 root 无法影响宿主机 | ❌ 低,容器 root 即宿主机 root |
| 挂载宿主机目录 | ⚠️ 需要 UID 映射,较复杂 | ✅ 直接挂载,权限无障碍 |
| Docker 嵌套 | ⚠️ 需要 nesting=1 + 额外配置 | ✅ 原生支持 |
| 推荐场景 | 默认选择,绝大多数场景 | 需要直接访问宿主机资源时 |
4.2 非特权容器的 UID 映射原理
非特权容器的核心机制是 UID/GID 映射。PVE 默认分配 65536 个 UID 的映射空间:
1
2
3
4
5
| # 查看容器的 UID 映射(在宿主机上)
cat /etc/pve/lxc/${CT_ID}.conf
# 输出类似:
# lxc.idmap: u 0 100000 65536
# lxc.idmap: g 0 100000 65536
|
这意味着容器内的 root (UID 0) 在宿主机上实际是 UID 100000,容器内的 UID 1000 在宿主机上是 UID 101000。
4.3 宿主机目录直通挂载的权限问题
这是非特权容器最常见的坑。假设你想把宿主机的 /data/media 挂载到容器内的 /media:
1
2
3
| # 宿主机上查看目录权限
ls -n /data/media
# drwxr-xr-x 2 1000 1000 4096 May 1 12:00 media
|
容器内的 UID 1000(普通用户)在宿主机上是 UID 101000,所以能正常读写。但如果宿主机上的目录属于 UID 0(root),容器内就需要 root 权限访问——恰好容器内 root 是 UID 100000,也匹配不上。
解决方案:在 CT_ID.conf 中自定义 UID 映射偏移,或改用 subuid / subgid 手动配置。更简单的办法:宿主机上将目录权限设为 777 或 chown 给对应映射后的 UID:
1
2
3
| # 在宿主机上执行
# 假设容器映射偏移是 100000,容器内 UID 1000 = 宿主机 UID 101000
chown -R 101000:101000 /data/media
|
或者直接在 pct.conf 里加挂载行:
1
2
| # 编辑 /etc/pve/lxc/${CT_ID}.conf
mp0: /data/media,mp=/media
|
PVE 会自动在宿主机侧做权限适配(大部分情况下)。
5. 在 LXC 容器中运行 Docker
很多 Homelab 玩家喜欢在 LXC 里嵌套 Docker。这里的关键词就是 nesting。
5.1 开启嵌套虚拟化
1
2
3
4
5
| # 创建时就开启 nesting
pct create 200 ... --features nesting=1
# 对已有容器,编辑 /etc/pve/lxc/${CT_ID}.conf 添加:
# features: nesting=1
|
5.2 非特权容器中装 Docker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 进入容器
pct enter 200
# 安装 Docker(Ubuntu 24.04 LTS)
apt update && apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update && apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 验证 Docker 正常
docker info | grep -i "server version"
|
5.3 ⚠️ 踩坑记录:非特权容器 Docker 存储驱动
非特权容器中 Docker 默认使用 overlay2 存储驱动,但在某些内核配置下会报错:
1
| Error response from daemon: error creating overlay mount to ...: operation not permitted
|
解决方案:改用 fuse-overlayfs 存储驱动:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 安装 fuse-overlayfs
apt install -y fuse-overlayfs
# 配置 Docker 使用 fuse-overlayfs
cat > /etc/docker/daemon.json <<EOF
{
"storage-driver": "fuse-overlayfs",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
EOF
systemctl restart docker
|
另一个方案:直接用特权容器跑 Docker。如果这台 Docker 主机是 Homelab 内网专用且你信任容器内的权限隔离,用特权容器更省心:
1
2
| pct create 200 ... --unprivileged 0
# 或在已创建的容器配置中加:unprivileged: 0
|
我个人做法是:专门一个特权 LXC 跑 Docker,不做其他用途。其他服务(DNS、数据库)跑在非特权容器里,互相隔离。
5.4 Docker Compose 项目落地
Docker 装好后,一个典型的 Homelab Docker Compose 项目结构:
1
2
| # 在容器内
mkdir -p /opt/stacks/{traefik,grafana,portainer}
|
参考之前文章中的 Traefik + Grafana 方案,将 docker-compose.yml 放在各自目录下即可。
6. 资源限制与性能调优
LXC 的优势之一就是精细的资源控制。
6.1 CPU 限制
1
2
3
4
5
6
| # 限制为 2 核
pct set 200 --cores 2
# CPU 权重(默认 100,越大优先级越高)
pct set 200 --cpulimit 2
pct set 200 --cpuunits 1024
|
6.2 内存限制
1
2
3
4
5
| # 硬限制 2GB,可超卖到 4GB(带 swap)
pct set 200 --memory 2048 --swap 2048
# 无 swap 的严格限制
pct set 200 --memory 2048 --swap 0
|
6.3 磁盘 I/O 限制
1
2
3
4
5
| # 限制 IOPS
pct set 200 --ioprio 7
# 限制带宽(MB/s)
pct set 200 --dev0 /dev/sda --bandwidth 100
|
6.4 查看容器资源使用
1
2
3
4
5
6
7
| # 实时查看所有容器的资源消耗
pct list
pct status 200
# 用 top 在容器内查看
pct enter 200
htop
|
7. 网络配置深入
7.1 静态 IP 与 DNS
创建时指定了网络配置,后续可在 Web UI 中修改或命令行调整:
1
2
3
4
5
| # 修改网络
pct set 200 --net0 name=eth0,bridge=vmbr0,ip=192.168.1.200/24,gw=192.168.1.1
# 添加第二个网卡(桥接到 vmbr1 用于隔离网络)
pct set 200 --net1 name=eth1,bridge=vmbr1,ip=10.0.1.200/24
|
7.2 容器内修改 DNS
LXC 的 DNS 配置在 /etc/resolv.conf。如果你用 DHCP,PVE 会自动覆盖。建议在 /etc/pve/lxc/${CT_ID}.conf 中设置:
1
2
| # 直接写入 CT 配置
echo "lxc.cgroup2.devices.allow: c 10:200 rwm" >> /etc/pve/lxc/200.conf
|
或者在容器内手动固定 DNS:
1
2
3
4
5
6
7
| # 容器内
cat > /etc/systemd/resolved.conf <<EOF
[Resolve]
DNS=192.168.1.1 8.8.8.8
FallbackDNS=1.1.1.1
EOF
systemctl restart systemd-resolved
|
8. 备份与恢复实战
8.1 手动备份 (vzdump)
1
2
3
4
5
6
7
8
| # 备份单个容器到 local 存储
vzdump 200 --dumpdir /var/lib/vz/dump --compress zstd
# 备份所有容器
vzdump --all --dumpdir /var/lib/vz/dump --compress zstd --mode snapshot
# 带排除的备份(排除不需要的挂载点)
vzdump 200 --dumpdir /var/lib/vz/dump --compress zstd --exclude-path /media
|
参数说明:
--compress zstd:使用 Zstandard 压缩,比 gzip 快 4x,压缩率接近--mode snapshot:快照模式,不停机备份(需要存储支持快照,如 ZFS、LVM-thin)--exclude-path:排除不需要备份的大目录(如媒体文件库)
8.2 定时自动备份
PVE 自带的备份调度在 Web UI 的 Datacenter → Backup 中设置。一个推荐的生产级配置:
1
2
3
4
5
6
7
8
9
| # 其实通过 Web UI 设置更直观
# 命令行创建备份 Job:
pvescheduler job create \
--type backup \
--ids 200,201,202 \
--mode snapshot \
--compress zstd \
--storage local \
--schedule "0 3 * * *" # 每天凌晨 3 点
|
8.3 从备份恢复
1
2
3
4
5
6
7
8
9
10
| # 查看可用备份
pveam list local
# 恢复容器(会重新创建,保留原有 ID 或指定新 ID)
pct restore 200 /var/lib/vz/dump/vzdump-lxc-200-2026_05_01-03_00_00.tar.zst \
--storage local-lvm --ignore-unpack-errors
# 恢复到新 ID(从备份复制容器,不覆盖原有容器)
pct restore 210 /var/lib/vz/dump/vzdump-lxc-200-2026_05_01-03_00_00.tar.zst \
--storage local-lvm
|
8.4 增量备份方案
LXC 本身不支持增量备份,但如果你用 ZFS 存储,可以利用 ZFS 快照实现"伪增量":
1
2
3
4
5
| # 手动创建 ZFS 快照
zfs snapshot rpool/data/vm-200-disk-0@backup-$(date +%Y%m%d)
# 配合 vzdump --mode snapshot 使用:vzdump 会自动创建快照、备份、删除快照
# 如果需要保存多个时间点的快照,自己写脚本管理
|
对于更专业的备份方案,推荐考虑 Proxmox Backup Server,它支持真正的增量备份和数据去重。等后面单独开一篇来写。
9. 生产级配置示例
最后给一个我目前在用的、生产环境级别的 LXC 容器配置组合:
Docker 主机容器(特权)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # /etc/pve/lxc/200.conf 的关键配置
arch: amd64
cores: 4
memory: 8192
swap: 0
features: keyctl=1,nesting=1,fuse=1
hostname: docker-host
net0: name=eth0,bridge=vmbr0,ip=192.168.1.200/24,gw=192.168.1.1
net1: name=eth1,bridge=vmbr1,ip=10.0.1.200/24
onboot: 1
ostype: ubuntu
protection: 1 # 防止误删
rootfs: local-lvm:vm-200-disk-0,size=100G
mp0: /data/docker,mp=/var/lib/docker # Docker 数据挂载到 ZFS 加速
unprivileged: 0 # 特权模式,简化 Docker 部署
|
DNS / 轻量服务容器(非特权)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # /etc/pve/lxc/201.conf
arch: amd64
cores: 1
memory: 512
swap: 256
features: nesting=0
hostname: dns-server
net0: name=eth0,bridge=vmbr0,ip=192.168.1.201/24,gw=192.168.1.1
onboot: 1
ostype: ubuntu
protection: 1
rootfs: local-lvm:vm-201-disk-0,size=8G
unprivileged: 1
# 挂载宿主机 certs 目录(需要在宿主机调整 UID 映射)
mp0: /etc/pve/nodes/pve1/ssl,mp=/etc/certs,ro
|
结语
LXC 容器是 Proxmox VE 中被低估的利器。用好它,你的 Homelab 可以在同样的硬件上跑更多的服务,而且管理成本远低于全 VM 方案。
关键要点回顾:
- 默认选非特权容器,需要 Docker 时再考虑特权模式(并做隔离)
- nesting=1 是在 LXC 中跑 Docker 的前提
- UID 映射 是理解目录挂载权限的钥匙
- vzdump + snapshot + zstd 是最佳备份组合
- 资源限制 要针对每台容器精细设置,不设限制等于共享资源的无政府状态
如果你正在从全 VM 方案转向混用 LXC + VM,或者刚开始搭建 PVE Homelab,希望这份指南能帮你少走弯路。有任何问题或更好的经验,欢迎在评论区交流!