diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
new file mode 100644
index 00000000..634a22cd
--- /dev/null
+++ b/.github/workflows/docker-release.yml
@@ -0,0 +1,74 @@
+name: Publish Docker Image
+
+on:
+ push:
+ tags:
+ - 'v*'
+
+concurrency:
+ group: docker-release-${{ github.ref }}
+ cancel-in-progress: false
+
+jobs:
+ publish:
+ if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-')
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - name: Checkout release tag
+ uses: actions/checkout@v4
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Derive image metadata
+ id: meta
+ run: |
+ VERSION="${GITHUB_REF_NAME#v}"
+ MINOR="${VERSION%.*}"
+ MAJOR="${VERSION%%.*}"
+ OWNER_LOWER=$(echo "${GITHUB_REPOSITORY_OWNER}" | tr '[:upper:]' '[:lower:]')
+ IMAGE="ghcr.io/${OWNER_LOWER}/ccs-dashboard"
+
+ {
+ echo "version=${VERSION}"
+ echo "minor=${MINOR}"
+ echo "major=${MAJOR}"
+ echo "image=${IMAGE}"
+ } >> "$GITHUB_OUTPUT"
+
+ - name: Log in to GitHub Container Registry
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Build and push release image
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ file: docker/Dockerfile
+ platforms: linux/amd64,linux/arm64
+ push: true
+ tags: |
+ ${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.version }}
+ ${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.minor }}
+ ${{ steps.meta.outputs.image }}:${{ steps.meta.outputs.major }}
+ ${{ steps.meta.outputs.image }}:latest
+ labels: |
+ org.opencontainers.image.title=ccs-dashboard
+ org.opencontainers.image.description=CCS Dashboard container image
+ org.opencontainers.image.url=https://github.com/${{ github.repository }}
+ org.opencontainers.image.source=https://github.com/${{ github.repository }}
+ org.opencontainers.image.version=${{ steps.meta.outputs.version }}
+ org.opencontainers.image.revision=${{ github.sha }}
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
diff --git a/README.md b/README.md
index 366d4df2..38321178 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ ccs config
Dashboard updates hub: `http://localhost:3000/updates`
-Want to run the dashboard in Docker? See `docker/README.md`.
+Want to run the dashboard in Docker or pull the prebuilt image? See `docker/README.md`.
### 3. Configure Your Accounts
diff --git a/docker/README.md b/docker/README.md
index d0204282..710afc7b 100644
--- a/docker/README.md
+++ b/docker/README.md
@@ -13,7 +13,24 @@ Persistent config, restart on reboot.
-## Quick Start (Docker Run)
+## Quick Start (Prebuilt Image)
+
+Pull the latest stable release image from GitHub Container Registry:
+
+```bash
+docker run -d \
+ --name ccs-dashboard \
+ --restart unless-stopped \
+ -p 3000:3000 \
+ -p 8317:8317 \
+ -e CCS_PORT=3000 \
+ -v ccs_home:/home/node/.ccs \
+ ghcr.io/kaitranntt/ccs-dashboard:latest
+```
+
+Release-tag images are also published as `ghcr.io/kaitranntt/ccs-dashboard:`.
+
+## Build Locally
```bash
docker build -f docker/Dockerfile -t ccs-dashboard:latest .