diff --git a/.github/workflows/docker-dxspider-proxy.yml b/.github/workflows/docker-dxspider-proxy.yml new file mode 100644 index 00000000..1a7d50b0 --- /dev/null +++ b/.github/workflows/docker-dxspider-proxy.yml @@ -0,0 +1,90 @@ +name: Build and Publish dxspider-proxy container image + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/dxspider-proxy + +on: + push: + branches: ['*'] + paths: + - 'dxspider-proxy/**' + - '.github/workflows/docker-dxspider-proxy.yml' + pull_request: + branches: + - Staging + - main + paths: + - 'dxspider-proxy/**' + - '.github/workflows/docker-dxspider-proxy.yml' + release: + types: [published] + # Allows running this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v4 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) + id: meta + uses: docker/metadata-action@v6 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,format=long + type=raw,value=latest,enable={{is_default_branch}} + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@v7 + with: + context: . + push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + annotations: ${{ steps.meta.outputs.annotations }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha,scope=${{ github.workflow }} + cache-to: type=gha,scope=${{ github.workflow }},mode=max + provenance: true + sbom: true + + - name: Generate artifact attestation + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: actions/attest-build-provenance@v4 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true diff --git a/.github/workflows/docker-iturhfprop-service.yml b/.github/workflows/docker-iturhfprop-service.yml new file mode 100644 index 00000000..d9ca339e --- /dev/null +++ b/.github/workflows/docker-iturhfprop-service.yml @@ -0,0 +1,90 @@ +name: Build and Publish iturhfprop-service container image + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/iturhfprop-service + +on: + push: + branches: ['*'] + paths: + - 'iturhfprop-service/**' + - '.github/workflows/docker-iturhfprop-service.yml' + pull_request: + branches: + - Staging + - main + paths: + - 'iturhfprop-service/**' + - '.github/workflows/docker-iturhfprop-service.yml' + release: + types: [published] + # Allows running this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v4 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) + id: meta + uses: docker/metadata-action@v6 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=sha,format=long + type=raw,value=latest,enable={{is_default_branch}} + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Build and push Docker image + id: push + uses: docker/build-push-action@v7 + with: + context: . + push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + annotations: ${{ steps.meta.outputs.annotations }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha,scope=${{ github.workflow }} + cache-to: type=gha,scope=${{ github.workflow }},mode=max + provenance: true + sbom: true + + - name: Generate artifact attestation + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: actions/attest-build-provenance@v4 + with: + subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + subject-digest: ${{ steps.push.outputs.digest }} + push-to-registry: true diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-openhamclock.yml similarity index 72% rename from .github/workflows/docker-image.yml rename to .github/workflows/docker-openhamclock.yml index 1a78e483..05a8f24e 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-openhamclock.yml @@ -1,22 +1,33 @@ -name: Build and Publish Docker Image +name: Build and Publish OHC container image + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} on: push: - branches: ['main'] + branches: ['*'] + paths-ignore: + - 'iturhfprop-service/**' + - 'dxspider-proxy/**' + pull_request: + branches: + - Staging + - main + paths-ignore: + - 'iturhfprop-service/**' + - 'dxspider-proxy/**' release: types: [published] + # Allows running this workflow manually from the Actions tab + workflow_dispatch: permissions: contents: read packages: write -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }} - jobs: build-and-push: - if: github.repository == 'accius/openhamclock' runs-on: ubuntu-latest permissions: contents: write @@ -26,16 +37,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Log in to the Container registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -43,13 +54,13 @@ jobs: - name: Extract metadata (tags, labels) id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=ref,event=pr - type=sha,prefix={{branch}}-,enable=${{ github.event_name != 'release' }} + type=sha,format=long type=raw,value=latest,enable={{is_default_branch}} type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} @@ -57,10 +68,10 @@ jobs: - name: Build and push Docker image id: push - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . - push: ${{ github.event_name != 'pull_request' }} + push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }} @@ -71,8 +82,8 @@ jobs: sbom: true - name: Generate artifact attestation - if: github.event_name != 'pull_request' - uses: actions/attest-build-provenance@v1 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + uses: actions/attest-build-provenance@v4 with: subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} subject-digest: ${{ steps.push.outputs.digest }} diff --git a/README.md b/README.md index 688752c1..0ac6923d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ OpenHamClock brings DX cluster spots, space weather, propagation predictions, PO ### Container Deployment Container images are available from the github container registry at `ghcr.io/accius/openhamclock` -Follow [these steps](#using-docker) for a docker-based deployment +Follow [the quick-start steps](docs/DOCKER.md#quick-start-zero-config) ### Local Install @@ -96,7 +96,7 @@ npm run dev ### Enabling Client-side Propagation Calculation (without having to build it) -If you want your clients (using your local server) to use the p533 modules for client side propagation calculation rather than the very rough estimates (which are based on band and time of day), run teh following as the user who can write to your repository. +If you want your clients (using your local server) to use the p533 modules for client side propagation calculation rather than the very rough estimates (which are based on band and time of day), run the following as the user who can write to your repository. ```bash scripts/fetch-wasm.sh @@ -1059,35 +1059,7 @@ The Pi setup script installs Node.js 22 LTS, clones the repository, builds the f ### Using Docker -**Docker Compose (recommended):** - -Grab `docker-compose.yml` and `.env.example` from the repo root, rename `.env.example` to `.env` and adjust the variables in `.env` to configure OpenHamClock. - -Start the container: - -```bash -docker compose up -d -``` - -**Manual Docker build:** - -```bash -docker build -t openhamclock . -docker run -d -p 3000:3000 -p 2237:2237/udp --name openhamclock openhamclock -``` - -The Dockerfile uses a multi-stage build: Stage 1 compiles the React frontend with Vite, Stage 2 creates a minimal production image with only the server and built assets. The UDP port mapping (`-p 2237:2237/udp`) is only needed if you use WSJT-X integration. - -**Environment variables:** Pass your configuration via Docker environment variables: - -```bash -docker run -d \ - -p 3000:3000 \ - -e CALLSIGN=K0CJH \ - -e LOCATOR=EN10 \ - -e HOST=0.0.0.0 \ - openhamclock -``` +Follow the [docker documentation](docs/DOCKER.md) ### Railway (Cloud) @@ -1223,10 +1195,7 @@ On local installs, you can also click the **UPDATE** button in the header to sta ### Docker -```bash -docker-compose pull -docker-compose up -d -``` +[Docker documentation on updating](docs/DOCKER.md#updating) ### Railway diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 534909b1..d7c9fadb 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -2,12 +2,22 @@ ## Quick Start (Zero Config) +Docker compose is the recommended way to deploy: + ```bash git clone https://github.com/OpenHamClock/openhamclock.git cd openhamclock docker compose up -d ``` +or you can also use traditional docker commands: + +```bash +docker run -d -p 3000:3000 --name openhamclock ghcr.io/accius/openhamclock:latest +``` + +This will pull the latest container image and start the OpenHamClock container + Open **** — that's it. OpenHamClock runs with sensible defaults. ## Customize Your Station @@ -71,9 +81,11 @@ If running behind nginx/Caddy/Traefik, you may want to override the health check HEALTH_ENDPOINT=http://localhost:3000/api/health ``` -## Volumes (Optional) +## Data persistence + +To persist stats and settings across container rebuilds you can either use volumes or bind mounts -To persist stats and settings across container rebuilds: +[Volumes](https://docs.docker.com/engine/storage/volumes/): ```yaml services: @@ -84,12 +96,43 @@ volumes: ohc-data: ``` +[Bind mounts](https://docs.docker.com/engine/storage/bind-mounts/): + +```yaml +services: + openhamclock: + volumes: + - ./ohc-data:/data +``` + ## Updating +Pull the latest image: + +```bash +# If using compose +docker compose pull + +# If using plain docker cli +docker pull ghcr.io/accius/openhamclock:latest +``` + +or build from the main branch: + ```bash git pull docker compose build --no-cache +``` + +then restart container with latest image: + +```bash +# If using compose docker compose up -d + +# If using plain docker cli +docker rm -f openhamclock +docker run -d -p 3000:3000 --name openhamclock ghcr.io/accius/openhamclock:latest ``` ## Configuration Priority @@ -114,3 +157,10 @@ See `.env.example` for the complete list with descriptions. Key sections: - **N1MM** — `N1MM_UDP_ENABLED`, `N1MM_UDP_PORT` - **Weather** — `OPENWEATHER_API_KEY`, `VITE_AMBIENT_*` - **Advanced** — `ITURHFPROP_URL`, `HEALTH_ENDPOINT`, `CORS_ORIGINS` + +## Other Microservices + +You can also self-host other microservices, check their respective documentation for details: + +- [iturhfprop-service](../iturhfprop-service/README.md#docker) +- [dxspider-proxy](../dxspider-proxy/README.md#docker) diff --git a/dxspider-proxy/README.md b/dxspider-proxy/README.md index cc5cf79e..359ffd6a 100644 --- a/dxspider-proxy/README.md +++ b/dxspider-proxy/README.md @@ -164,9 +164,33 @@ The service will automatically start and connect to DX Spider. ### Docker +#### Compose + +When using a compose file for OpenHamClock, you can add it as an additional service: + +```yaml +services: + openhamclock: ... + + dxspider-proxy: + build: + context: ./dxspider-proxy # path to this subdirectory in the openhamclock repo + image: ghcr.io/accius/dxspider-proxy:latest + restart: unless-stopped + container_name: dxspider-proxy +``` + +#### CLI + ```bash +# Build docker build -t dxspider-proxy . -docker run -p 3001:3001 -e CALLSIGN=YOURCALL dxspider-proxy + +# or Pull +docker pull ghcr.io/accius/dxspider-proxy:latest + +# Run +docker run -p 3001:3001 -e CALLSIGN=YOURCALL ghcr.io/accius/dxspider-proxy:latest ``` ### Local Development @@ -178,7 +202,9 @@ CALLSIGN=YOURCALL npm start ## Using with OpenHamClock -Once deployed, update your OpenHamClock configuration to use this proxy as a DX cluster source: +If using docker, edit the `DXSPIDER_PROXY_URL` variable in your `.env` file + +else, update your OpenHamClock configuration to use this proxy as a DX cluster source: ```text https://your-proxy-url.railway.app/api/dxcluster/spots diff --git a/iturhfprop-service/README.md b/iturhfprop-service/README.md index ca005216..a150cfc0 100644 --- a/iturhfprop-service/README.md +++ b/iturhfprop-service/README.md @@ -111,16 +111,7 @@ The Dockerfile will: ### Docker -```bash -# Build -docker build -t iturhfprop-service . - -# Run -docker run -p 3000:3000 iturhfprop-service - -# Test -curl http://localhost:3000/api/health -``` +#### Compose If using a compose file, you can also add it as an additional service: @@ -131,10 +122,27 @@ services: iturhfprop: build: context: ./iturhfprop-service # path to this subdirectory in the openhamclock repo + image: ghcr.io/accius/iturhfprop-service:latest restart: unless-stopped container_name: iturhfprop ``` +#### CLI + +```bash +# Build +docker build -t iturhfprop-service . + +# or Pull +docker pull ghcr.io/accius/iturhfprop-service:latest + +# Run +docker run -p 3000:3000 ghcr.io/accius/iturhfprop-service:latest + +# Test +curl http://localhost:3000/api/health +``` + And then enable it in your `.env` file as: `ITURHFPROP_URL=http://iturhfprop:3000` @@ -167,7 +175,9 @@ npm start ## Integration with OpenHamClock -In your OpenHamClock server.js, add: +If using docker, edit the `ITURHFPROP_URL` variable in your `.env` file. + +If running locally, in your OpenHamClock server.js, add: ```javascript const ITURHFPROP_SERVICE = process.env.ITURHFPROP_URL || 'http://localhost:3001';