> ## Documentation Index
> Fetch the complete documentation index at: https://starforge.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 用 Docker Compose 部署

> Postgres、Redis 和控制台跑在一台机器上——最快的一套真实部署。

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
git clone https://github.com/wccdev/starforge && cd starforge
./deploy/up-prod.sh
```

这个脚本会把 `.env.example` 复制成 `.env`，把仍是模板值的密码和 JWT 密钥换成随机值，然后把整栈拉起来。
它是幂等的——改完 `.env` 再跑一次即可。

## 会起来哪些服务

| 服务         | 镜像                                  | 为什么                                     |
| ---------- | ----------------------------------- | --------------------------------------- |
| `postgres` | `timescale/timescaledb:latest-pg18` | 台账。用 TimescaleDB 是因为指标点和硬件点是 hypertable |
| `redis`    | `redis:latest`                      | 共享缓存、分布式锁、限流、token 即时吊销                 |
| `app`      | `starforge-console:latest`          | FastAPI 加构建好的控制台，同一个镜像                  |

<Warning>
  Postgres 必须带着 `shared_preload_libraries=timescaledb` 启动，compose 文件里已经设了。
  但如果数据卷最初是用普通 `postgres` 初始化的，它的 `postgresql.conf` 里不会有这一项，
  hypertable 迁移就会失败。要么从干净的卷开始，要么手动补上这项设置。
</Warning>

## 开始之前

<Steps>
  <Step title="设定存储根">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    FORGE_STORAGE_ROOT=/srv/starforge
    ```

    Compose 会把它以**完全相同的路径** bind-mount 进容器。这不是为了好看：
    用 `local` 执行器时控制台驱动的是宿主机的 Docker daemon，`-v` 左边那个是宿主机路径。
    两边不一致，控制台就看不到作业写了什么。
  </Step>

  <Step title="把 docker 组给容器">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    getent group docker | cut -d: -f3     # 把这个数字填进 DOCKER_GID
    ```

    控制台以 uid/gid 10001 运行，需要能写 `/var/run/docker.sock`。
  </Step>

  <Step title="决定 GPU 怎么处理">
    compose 文件里写了 `gpus: all`，让 NVIDIA toolkit 把 `nvidia-smi` 和驱动库注入进来——
    控制台能**看见**卡，但不占用它们。

    没有 NVIDIA 运行时的机器上，删掉 `gpus: all` 和 `NVIDIA_DRIVER_CAPABILITIES` 那两行，
    然后显式设 `FORGE_LOCAL_GPU_COUNT`。
  </Step>

  <Step title="启动">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    docker compose up -d --build
    docker compose logs -f app
    ```
  </Step>
</Steps>

## 内网域名

容器不读宿主机的 `/etc/hosts`。如果你的 OIDC 提供方或镜像站只能靠那份文件解析，就补上映射：

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
cp docker-compose.override.example.yml docker-compose.override.yml
# 编辑 extra_hosts，IP 用宿主机 `getent hosts <名字>` 的结果
docker compose up -d --force-recreate app
```

## 推荐配置

其余项都有可用的默认值，这几项没有。

```bash .env theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
FORGE_DEFAULT_FLEET_KIND=local
FORGE_STORAGE_ROOT=/srv/starforge
FORGE_PUBLIC_URL=https://starforge.your-company.com
FORGE_WEB_JWT_SECRET=<openssl rand -hex 32>
FORGE_DB_URL=postgresql+psycopg://forge:<password>@postgres:5432/forge
FORGE_REDIS_URL=redis://:<password>@redis:6379/0
FORGE_INGEST_URL=http://<训练容器能访问到的地址>:8080
```

<Info>
  `FORGE_INGEST_URL` 是最容易配错的一项。训练容器要往它 POST 指标，
  而在 `local` 执行器下它们共用宿主机网络——所以 `127.0.0.1` 在那里能用，
  但你一加第二台机器它就失效了。一开始就填宿主机的真实地址。
</Info>

数据库和 Redis 的主机名是 compose 服务名，只在 `starforge-internal` 网络内可解析。
整栈用 compose 拉起来；单独 `docker run` 应用镜像会报 `failed to resolve host 'postgres'`。

## 确认成功

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -s http://localhost:8080/api/version
docker compose ps          # 三个服务都 healthy
```

然后打开控制台。首次访问会走引导流程创建第一个管理员。

## 从一台机器往外走

Compose 加 `local` 后端的上限就是一台主机。不够用时，做法是再注册一个 Fleet，而不是做一次迁移：
用 [node](/zh-Hans/ops/executor-node) Fleet 管几台裸机，
或者把控制台本身搬进 [Kubernetes](/zh-Hans/ops/install-kubernetes)。已经在第一个 Fleet 上的作业
不用动。
