服务器目录结构规范
2026/3/20大约 4 分钟
服务器目录结构规范
提示
统一的目录结构是服务器可维护性的基石。任何人接手都能快速找到配置、日志、应用在哪里。
核心原则
- 约定优于配置:大家都遵循同一套规范,减少沟通成本
- 职责分离:应用代码、配置、日志、数据各有其位
- 权限清晰:目录权限与用户权限对应
推荐的目录结构
/
├── opt/
│ └── apps/ # 所有应用的根目录
│ ├── blog/ # 具体应用
│ │ ├── current/ # 当前运行版本(软链接)
│ │ ├── releases/ # 历史版本
│ │ │ ├── 20240115-001/
│ │ │ └── 20240116-001/
│ │ ├── shared/ # 共享文件(跨版本)
│ │ │ ├── .env
│ │ │ ├── uploads/
│ │ │ └── logs -> /var/log/apps/blog
│ │ └── repo/ # Git 仓库(可选)
│ └── api/
│ └── ...
│
├── var/
│ ├── log/
│ │ └── apps/ # 应用日志
│ │ ├── blog/
│ │ │ ├── app.log
│ │ │ └── error.log
│ │ └── api/
│ └── data/
│ └── apps/ # 应用数据
│ ├── blog/
│ └── api/
│
├── etc/
│ ├── nginx/
│ │ ├── nginx.conf
│ │ ├── sites-available/ # 所有站点配置
│ │ └── sites-enabled/ # 已启用站点(软链接)
│ └── systemd/
│ └── system/ # 自定义服务文件
│ └── app-blog.service
│
└── srv/ # 静态资源(可选)
└── www/
└── blog/
各目录详解
/opt/apps/ - 应用目录
这是所有业务应用的家,每个应用一个子目录。
# 创建应用目录结构
mkdir -p /opt/apps/blog/{releases,shared}
mkdir -p /opt/apps/blog/shared/{uploads,config}
# 设置所有者
chown -R app-blog:apps /opt/apps/blog
# 设置权限
chmod 750 /opt/apps/blog
chmod 770 /opt/apps/blog/shared/uploads
版本发布目录结构
/opt/apps/blog/
├── current -> releases/20240116-001 # 软链接指向当前版本
├── releases/
│ ├── 20240115-001/ # 第一次发布
│ ├── 20240115-002/ # 当天第二次
│ └── 20240116-001/ # 最新版本
└── shared/
├── .env # 环境变量(软链接到 current)
├── uploads/ # 上传文件
└── node_modules/ # 依赖(可选共享)
版本目录命名规则:YYYYMMDD-序号,如 20240116-001
发布流程:
# 1. 创建新版本目录
NEW_RELEASE="$(date +%Y%m%d)-001"
mkdir -p /opt/apps/blog/releases/$NEW_RELEASE
# 2. 部署代码到新版本目录
rsync -av --exclude='.git' ./dist/ /opt/apps/blog/releases/$NEW_RELEASE/
# 3. 链接共享文件
ln -sf /opt/apps/blog/shared/.env /opt/apps/blog/releases/$NEW_RELEASE/.env
ln -sf /opt/apps/blog/shared/uploads /opt/apps/blog/releases/$NEW_RELEASE/uploads
# 4. 切换软链接(原子操作)
ln -sfn /opt/apps/blog/releases/$NEW_RELEASE /opt/apps/blog/current
# 5. 重启服务
sudo systemctl restart app-blog
# 6. 清理旧版本(保留最近 5 个)
cd /opt/apps/blog/releases
ls -t | tail -n +6 | xargs rm -rf
/var/log/apps/ - 日志目录
# 创建日志目录
mkdir -p /var/log/apps/blog
# 设置权限(应用可写,其他人可读)
chown app-blog:apps /var/log/apps/blog
chmod 755 /var/log/apps/blog
# 在应用目录创建软链接
ln -sf /var/log/apps/blog /opt/apps/blog/shared/logs
日志文件规范:
/var/log/apps/blog/
├── app.log # 应用主日志
├── error.log # 错误日志
├── access.log # 访问日志(如果需要)
└── app.log.1 # 轮转后的日志
/var/data/apps/ - 数据目录
存放应用产生的持久化数据(非代码、非日志)。
mkdir -p /var/data/apps/blog
chown app-blog:apps /var/data/apps/blog
chmod 750 /var/data/apps/blog
文件权限速查
常用权限值
| 权限 | 数字 | 含义 | 适用场景 |
|---|---|---|---|
rwxr-xr-x | 755 | 所有者完全控制,其他人可读可执行 | 可执行文件、公开目录 |
rwxr-x--- | 750 | 所有者完全控制,组可读可执行 | 应用目录 |
rw-r--r-- | 644 | 所有者可读写,其他人只读 | 配置文件、普通文件 |
rw-r----- | 640 | 所有者可读写,组只读 | 敏感配置 |
rw------- | 600 | 仅所有者可读写 | 密钥、密码文件 |
rwxrwx--- | 770 | 所有者和组完全控制 | 共享上传目录 |
应用场景示例
# 应用根目录
chmod 750 /opt/apps/blog
# drwxr-x--- app-blog apps
# 配置文件
chmod 640 /opt/apps/blog/shared/.env
# -rw-r----- app-blog apps
# 私钥文件
chmod 600 /opt/apps/blog/shared/ssl/private.key
# -rw------- app-blog apps
# 上传目录(允许应用写入)
chmod 770 /opt/apps/blog/shared/uploads
# drwxrwx--- app-blog apps
# 日志文件
chmod 644 /var/log/apps/blog/app.log
# -rw-r--r-- app-blog apps
实战脚本
初始化应用目录
#!/bin/bash
# init-app-dir.sh - 初始化应用目录结构
# 用法: ./init-app-dir.sh <app-name>
set -e
APP_NAME=${1:?请提供应用名称}
APP_USER="app-${APP_NAME}"
APP_DIR="/opt/apps/${APP_NAME}"
LOG_DIR="/var/log/apps/${APP_NAME}"
DATA_DIR="/var/data/apps/${APP_NAME}"
echo "=== 初始化应用: ${APP_NAME} ==="
# 创建应用用户
if ! id "$APP_USER" &>/dev/null; then
useradd --system --shell /usr/sbin/nologin --no-create-home "$APP_USER"
usermod -aG apps "$APP_USER"
echo "创建用户: $APP_USER"
fi
# 创建目录结构
mkdir -p "${APP_DIR}"/{releases,shared/{config,uploads}}
mkdir -p "$LOG_DIR" "$DATA_DIR"
# 创建日志软链接
ln -sf "$LOG_DIR" "${APP_DIR}/shared/logs"
# 设置所有者
chown -R "$APP_USER":apps "$APP_DIR" "$LOG_DIR" "$DATA_DIR"
# 设置权限
chmod 750 "$APP_DIR"
chmod 770 "${APP_DIR}/shared/uploads"
chmod 755 "$LOG_DIR"
chmod 750 "$DATA_DIR"
echo "=== 目录结构初始化完成 ==="
tree -L 3 "$APP_DIR" 2>/dev/null || ls -la "$APP_DIR"
清理旧版本脚本
#!/bin/bash
# cleanup-releases.sh - 清理旧版本,保留最近 N 个
# 用法: ./cleanup-releases.sh <app-name> [保留数量]
APP_NAME=${1:?请提供应用名称}
KEEP=${2:-5}
RELEASES_DIR="/opt/apps/${APP_NAME}/releases"
if [ ! -d "$RELEASES_DIR" ]; then
echo "目录不存在: $RELEASES_DIR"
exit 1
fi
cd "$RELEASES_DIR"
TOTAL=$(ls -1 | wc -l)
TO_DELETE=$((TOTAL - KEEP))
if [ "$TO_DELETE" -gt 0 ]; then
echo "保留最近 $KEEP 个版本,删除 $TO_DELETE 个旧版本"
ls -t | tail -n "$TO_DELETE" | xargs -I{} rm -rf "{}"
echo "清理完成"
else
echo "当前 $TOTAL 个版本,无需清理"
fi
常见错误
❌ 错误做法
# 1. 项目散落各处
/home/user/blog
/root/projects/api
/var/www/html/site
# 2. 直接在 current 目录修改代码
vim /opt/apps/blog/current/config.js # 下次部署会丢失!
# 3. 权限设置太松
chmod 777 /opt/apps/blog # 危险!
# 4. 日志和代码混在一起
/opt/apps/blog/current/logs/ # 应该分离
✅ 正确做法
# 1. 统一存放
/opt/apps/blog
/opt/apps/api
/opt/apps/site
# 2. 修改共享文件或重新部署
vim /opt/apps/blog/shared/.env # 共享配置
# 或重新部署新版本
# 3. 最小权限
chmod 750 /opt/apps/blog
chmod 640 /opt/apps/blog/shared/.env
# 4. 日志分离
/var/log/apps/blog/app.log