Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions .github/workflows/docker-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Build Docker Images

on:
push:
branches:
- main
tags:
- "v*"
paths:
- ".dockerignore"
- ".github/workflows/docker-images.yml"
- "docker-compose.build.yml"
- "docker-compose.yml"
- "infrastructure/Dockerfile.api"
- "infrastructure/Dockerfile.web"
- "package-lock.json"
- "package.json"
- "packages/api/**"
- "packages/shared/**"
- "packages/web-next/**"
- "tsconfig.base.json"
pull_request:
paths:
- ".dockerignore"
- ".github/workflows/docker-images.yml"
- "docker-compose.build.yml"
- "docker-compose.yml"
- "infrastructure/Dockerfile.api"
- "infrastructure/Dockerfile.web"
- "package-lock.json"
- "package.json"
- "packages/api/**"
- "packages/shared/**"
- "packages/web-next/**"
- "tsconfig.base.json"
workflow_dispatch:
inputs:
platforms:
description: "Target platforms, e.g. linux/amd64 or linux/amd64,linux/arm64"
required: false
default: ""

permissions:
contents: read
packages: write

env:
REGISTRY: ghcr.io
DEFAULT_PLATFORMS: linux/amd64

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- name: api
image: synapse-api
dockerfile: infrastructure/Dockerfile.api
- name: web
image: synapse-web
dockerfile: infrastructure/Dockerfile.web

steps:
- uses: actions/checkout@v4

- name: Resolve image namespace
id: namespace
shell: bash
run: |
owner="${GITHUB_REPOSITORY_OWNER,,}"
echo "owner=${owner}" >> "$GITHUB_OUTPUT"

- name: Resolve target platforms
id: platforms
shell: bash
env:
INPUT_PLATFORMS: ${{ github.event_name == 'workflow_dispatch' && inputs.platforms || '' }}
REPO_PLATFORMS: ${{ vars.DOCKER_IMAGE_PLATFORMS || '' }}
run: |
platforms="${INPUT_PLATFORMS:-${REPO_PLATFORMS:-${DEFAULT_PLATFORMS}}}"
echo "value=${platforms}" >> "$GITHUB_OUTPUT"
echo "Building ${{ matrix.name }} for ${platforms}"

- uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ steps.namespace.outputs.owner }}/${{ matrix.image }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=ref,event=tag
type=sha,prefix=sha-

- name: Build and publish image
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: ${{ steps.platforms.outputs.value }}
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.name }}
cache-to: ${{ github.event_name != 'pull_request' && format('type=gha,scope={0},mode=max', matrix.name) || '' }}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ This repository currently ships with a self-hosting path centered on a single Ub
- `systemd` for the API and desktop web services
- `nginx` as the public entrypoint
- Dockerized PostgreSQL and Redis for local infrastructure
- prebuilt GHCR images for optional containerized API and desktop web services
- desktop web served from `packages/web-next`
- mobile web exported as static assets from `packages/mobile-app`

Expand Down
1 change: 1 addition & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ npm run web
- `systemd` 负责 API 与桌面 Web 进程
- `nginx` 作为公网入口
- PostgreSQL 与 Redis 通过 Docker 运行
- API 与桌面 Web 也提供预构建 GHCR 镜像,可选容器化运行
- 桌面端由 `packages/web-next` 提供
- mobile web 由 `packages/mobile-app` 静态导出

Expand Down
1 change: 1 addition & 0 deletions README_ES.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ Hoy el repositorio trae una ruta de autoalojamiento pensada para una sola máqui
- `systemd` para la API y la web de escritorio
- `nginx` como punto de entrada público
- PostgreSQL y Redis en Docker
- imágenes precompiladas en GHCR para ejecutar opcionalmente la API y la web de escritorio en contenedores
- la web de escritorio servida desde `packages/web-next`
- el mobile web exportado estáticamente desde `packages/mobile-app`

Expand Down
20 changes: 20 additions & 0 deletions deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This repository is deployed on a single Ubuntu host with:
- local `nginx`
- `systemd` for API and web
- Docker for PostgreSQL and Redis
- Prebuilt GHCR images for the API and desktop web containers
- desktop web on a production Next.js server
- optional localhost-only Next dev server for remote debugging over SSH

Expand Down Expand Up @@ -63,6 +64,25 @@ sudo docker compose up -d postgres redis
Notes:

- Redis is bound to loopback and requires a password from `.env`
- The production compose profile can also run the API and desktop web from prebuilt images:

```bash
sudo docker compose --profile production pull api web
sudo docker compose --profile production up -d postgres redis api web
```

By default these services use:

- `ghcr.io/zai-org/synapse-api:${SYNAPSE_IMAGE_TAG:-latest}`
- `ghcr.io/zai-org/synapse-web:${SYNAPSE_IMAGE_TAG:-latest}`

Set `SYNAPSE_IMAGE_TAG`, `SYNAPSE_API_IMAGE`, or `SYNAPSE_WEB_IMAGE` in `.env` to pin a release or use a different registry.

If you need to build the application images locally instead of pulling from GHCR, use the build override:

```bash
sudo docker compose -f docker-compose.yml -f docker-compose.build.yml --profile production up -d --build postgres redis api web
```

## 4. Application dependencies

Expand Down
10 changes: 10 additions & 0 deletions docker-compose.build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
api:
build:
context: .
dockerfile: infrastructure/Dockerfile.api

web:
build:
context: .
dockerfile: infrastructure/Dockerfile.web
8 changes: 2 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ services:
retries: 5

api:
build:
context: .
dockerfile: infrastructure/Dockerfile.api
image: ${SYNAPSE_API_IMAGE:-ghcr.io/zai-org/synapse-api}:${SYNAPSE_IMAGE_TAG:-latest}
container_name: synapse-api
environment:
DATABASE_URL: postgresql://${POSTGRES_USER:-synapse}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB:-synapse}
Expand All @@ -68,9 +66,7 @@ services:
- production

web:
build:
context: .
dockerfile: infrastructure/Dockerfile.web
image: ${SYNAPSE_WEB_IMAGE:-ghcr.io/zai-org/synapse-web}:${SYNAPSE_IMAGE_TAG:-latest}
container_name: synapse-web
environment:
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL}
Expand Down
8 changes: 5 additions & 3 deletions infrastructure/Dockerfile.api
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ WORKDIR /app
COPY package.json package-lock.json ./
COPY packages/shared/package.json packages/shared/
COPY packages/api/package.json packages/api/
RUN npm ci --workspace=packages/shared --workspace=packages/api
# CPU runtime files are bundled by onnxruntime-node; skip optional CUDA provider downloads.
RUN ONNXRUNTIME_NODE_INSTALL=skip ONNXRUNTIME_NODE_INSTALL_CUDA=skip \
npm ci --workspace=packages/shared --workspace=packages/api
COPY tsconfig.base.json ./
COPY packages/shared/ packages/shared/
COPY packages/api/ packages/api/
RUN npx tsc -b packages/shared
RUN npm run build -w packages/shared && npm run build -w packages/api

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/ ./
EXPOSE 3001
CMD ["npx", "tsx", "packages/api/src/index.ts"]
CMD ["node", "packages/api/dist/index.js"]
33 changes: 20 additions & 13 deletions infrastructure/Dockerfile.web
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
FROM node:20-alpine AS builder
WORKDIR /app

COPY packages/shared /app/packages/shared
WORKDIR /app/packages/shared
RUN npm install
RUN npm run build
COPY package.json package-lock.json ./
COPY packages/shared/package.json packages/shared/
COPY packages/web-next/package.json packages/web-next/
RUN npm ci --workspace=packages/shared --workspace=packages/web-next

COPY packages/web-next /app/packages/web-next
WORKDIR /app/packages/web-next
RUN npm ci
RUN npm run build
COPY tsconfig.base.json ./
COPY packages/shared/ packages/shared/
COPY packages/web-next/ packages/web-next/
RUN npm run build -w packages/shared
RUN npm run build -w packages/web-next

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/packages/web-next/.next ./.next
COPY --from=builder /app/packages/web-next/public ./public
COPY --from=builder /app/packages/web-next/node_modules ./node_modules
COPY --from=builder /app/packages/web-next/package.json ./
ENV NODE_ENV=production
COPY --from=builder /app/package.json ./package.json
COPY --from=builder /app/package-lock.json ./package-lock.json
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/packages/shared/package.json ./packages/shared/package.json
COPY --from=builder /app/packages/shared/dist ./packages/shared/dist
COPY --from=builder /app/packages/web-next/.next ./packages/web-next/.next
COPY --from=builder /app/packages/web-next/next.config.mjs ./packages/web-next/next.config.mjs
COPY --from=builder /app/packages/web-next/package.json ./packages/web-next/package.json
COPY --from=builder /app/packages/web-next/public ./packages/web-next/public
EXPOSE 3000
CMD ["npx", "next", "start", "-p", "3000"]
CMD ["npm", "run", "start", "-w", "packages/web-next", "--", "-p", "3000"]
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dev:daemon": "npm run dev -w packages/remote-agent-daemon",
"dev:web": "npm run dev -w packages/web-next",
"dev:mock-chat": "node .workbuddy/mock-chat-completions-server.cjs",
"prepare": "git config core.hooksPath .githooks",
"prepare": "(command -v git >/dev/null 2>&1 && git rev-parse --git-dir >/dev/null 2>&1 && git config core.hooksPath .githooks) || true",
"format": "node scripts/format-files.mjs --all",
"format:check": "node scripts/format-files.mjs --all --check",
"format:staged": "lint-staged",
Expand Down
1 change: 1 addition & 0 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"dev": "tsc --watch"
},
"devDependencies": {
"@types/node": "^25.1.0",
"typescript": "^5.4.0"
}
}