Skip to content
Merged
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
81 changes: 81 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Docker image

on:
push:
branches: [main]
release:
types: [published]
pull_request:
types: [opened, reopened, synchronize, labeled, unlabeled]
workflow_dispatch:

permissions:
contents: read
packages: write
pull-requests: read

concurrency:
group: docker-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
docker:
if: >-
${{ github.event_name != 'pull_request' ||
contains(github.event.pull_request.labels.*.name, 'build') ||
github.event.label.name == 'build' }}
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Read application version
id: version
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
shell: bash
run: echo "value=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"

- name: Compute image metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=raw,value=${{ steps.version.outputs.value }},enable=${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
type=raw,value=latest,enable=${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
type=ref,event=pr
type=sha,prefix=sha-

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

- name: Build and publish (or validate PR build)
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ Set `BETTER_AUTH_SECRET` in `.env`. Either set `DATABASE_URL` (your Postgres; no

---

## Docker images

Prebuilt multi-architecture images are published to `ghcr.io/zhravan/proxydeck` for `linux/amd64` and `linux/arm64`.

See **[Docker image deployment](docs/docker.md)** for pull commands, a complete Docker Compose example with bundled PostgreSQL, version pinning, and CI/CD publishing behavior.

---

<details>
<summary><b>Click to view .env variable definitions</b></summary>

Expand Down
5 changes: 5 additions & 0 deletions deploy/.env.ghcr.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copy to .env and replace both placeholders before starting.
BETTER_AUTH_SECRET=replace-with-a-long-random-secret
BETTER_AUTH_URL=http://localhost:3000
PORT=3000
POSTGRES_PASSWORD=change-this-database-password
34 changes: 34 additions & 0 deletions deploy/docker-compose.ghcr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
services:
postgres:
image: postgres:16-alpine
profiles: ["db"]
restart: unless-stopped
environment:
POSTGRES_USER: proxydeck
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env}
POSTGRES_DB: proxydeck
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U proxydeck -d proxydeck"]
interval: 5s
timeout: 5s
retries: 10
start_period: 10s
app:
image: ghcr.io/zhravan/proxydeck:${PROXYDECK_VERSION:-latest}
pull_policy: always
restart: unless-stopped
ports:
- "${PORT:-3000}:3000"
environment:
PORT: 3000
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?Set BETTER_AUTH_SECRET in .env}
BETTER_AUTH_URL: ${BETTER_AUTH_URL:-http://localhost:3000}
DATABASE_URL: ${DATABASE_URL:-postgres://proxydeck:${POSTGRES_PASSWORD}@postgres:5432/proxydeck}
depends_on:
postgres:
condition: service_healthy
required: false
volumes:
postgres_data: {}
75 changes: 75 additions & 0 deletions docs/docker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Docker images

ProxyDeck publishes multi-platform container images to GitHub Container Registry (GHCR).

- Image: `ghcr.io/zhravan/proxydeck`
- Platforms: `linux/amd64` and `linux/arm64`
- Main-branch tags: the version in `package.json` and `latest`.
- Release tags: semantic version tags (`1.2.3`, `1.2`, `1`) from GitHub Releases.
- The image uses the repository `Dockerfile`; its entrypoint runs Drizzle migrations before starting the server.

## Pull and run

The GHCR package is intended to be public. Once the package has been made public, pull without authenticating:

```bash
docker pull ghcr.io/zhravan/proxydeck:latest
```

A standalone container needs an existing PostgreSQL database reachable from the container:

```bash
docker run -d --name proxydeck -p 3000:3000 \
-e BETTER_AUTH_SECRET="$(openssl rand -base64 32)" \
-e BETTER_AUTH_URL="http://localhost:3000" \
-e DATABASE_URL="postgres://USER:PASSWORD@HOST:5432/proxydeck" \
--restart unless-stopped \
ghcr.io/zhravan/proxydeck:latest
```

## Complete setup with Docker Compose

Download the checked-in Compose and environment examples:

```bash
mkdir proxydeck && cd proxydeck
curl -fsSLO https://raw.githubusercontent.com/zhravan/proxydeck/main/deploy/docker-compose.ghcr.yml
curl -fsSLO https://raw.githubusercontent.com/zhravan/proxydeck/main/deploy/.env.ghcr.example
cp .env.ghcr.example .env
```

Generate secrets and replace the placeholders in `.env` (edit the file with your preferred editor):

```bash
printf 'BETTER_AUTH_SECRET=%s\n' "$(openssl rand -base64 32)"
printf 'POSTGRES_PASSWORD=%s\n' "$(openssl rand -hex 24)"
```

Put those generated values into `.env`, then start ProxyDeck:

```bash
docker compose -f docker-compose.ghcr.yml --env-file .env --profile db up -d
docker compose -f docker-compose.ghcr.yml --env-file .env ps
```

Open [http://localhost:3000](http://localhost:3000). The app waits for PostgreSQL and automatically applies database migrations on startup. PostgreSQL data persists in the `postgres_data` named volume.

Upgrade:

```bash
docker compose -f docker-compose.ghcr.yml --env-file .env --profile db pull
docker compose -f docker-compose.ghcr.yml --env-file .env --profile db up -d
```

Pin a release by setting `PROXYDECK_VERSION=0.1.8` in `.env` (use any published version tag). For an external database, set `DATABASE_URL` in `.env` and omit `--profile db`.

## CI and publishing

The `.github/workflows/docker.yml` workflow:

1. Builds and publishes `linux/amd64` and `linux/arm64` after every push/merge to `main`.
2. Tags main builds with the current `package.json` version and `latest`.
3. Publishes semantic version tags for published GitHub Releases.
4. Builds pull requests only after the `build` label is applied. Once labeled, every subsequent commit triggers another build. PR builds are validation-only and never push to GHCR.
5. Uses the built-in `GITHUB_TOKEN`; no PAT is required. Ensure **Settings → Actions → General → Workflow permissions** permits write access for packages. Set the GHCR package visibility to public after its first publication if anonymous pulls are desired.

Loading