docs: add auto-update watcher and deploy scripts for nginx-proxy setup
Hee Sung Son committed
Mar 9, 2026 at 18:51 UTC
3e77f77788a4daf788bca7a3198f47c569e6f51c
4 files changed
+139
-2
docs/deployment.md
+83
-2
@@ -165,9 +165,90 @@ If your proxy source addresses are public or you want a stricter allowlist, also
165
docker compose up
166
```
167
168
-## 5. Troubleshooting
168
+## 5. Auto-Update
169
170
-### 5.1 Ports blocked
170
+Automatically redeploy when a new `ghcr.io/gosuda/portal:latest` image is pushed.
171
+
172
+### 5.1 Deploy script
173
+
174
+Create `deploy_portal.sh` in your project directory:
175
+
176
+```bash
177
+#!/usr/bin/env bash
178
+set -euo pipefail
179
+
180
+cd "$(dirname "$0")"
181
+
182
+docker compose pull
183
+docker compose up -d
184
+docker image prune -f
185
+```
186
+
187
+### 5.2 Watcher script
188
+
189
+The repository includes `watch_and_deploy.sh`, which polls the remote image digest and runs the deploy script on change.
190
+
191
+Environment variables:
192
+
193
+| Variable | Default | Description |
194
+|---|---|---|
195
+| `INTERVAL` | `60` | Poll interval in seconds |
196
+| `DEPLOY_SCRIPT` | `deploy_portal.sh` | Path to deploy script |
197
+| `DIGEST_FILE` | `.portal_image_digest` | File storing the last known digest |
198
+
199
+### 5.3 Register as systemd service
200
+
201
+Set `WorkingDirectory` and `ExecStart` to the directory where `watch_and_deploy.sh` and `deploy_portal.sh` are located:
202
+
203
+```bash
204
+sudo tee /etc/systemd/system/portal-watcher.service << 'EOF'
205
+[Unit]
206
+Description=Portal Docker Image Watcher
207
+After=network-online.target docker.service
208
+Wants=network-online.target
209
+Requires=docker.service
210
+
211
+[Service]
212
+Type=simple
213
+User=opc
214
+# Set to the directory containing watch_and_deploy.sh and deploy_portal.sh
215
+WorkingDirectory=<path-to-project>
216
+ExecStart=/bin/bash <path-to-project>/watch_and_deploy.sh
217
+Restart=always
218
+RestartSec=10
219
+Environment=INTERVAL=60
220
+Environment=DEPLOY_SCRIPT=deploy_portal.sh
221
+
222
+[Install]
223
+WantedBy=multi-user.target
224
+EOF
225
+
226
+sudo systemctl daemon-reload
227
+sudo systemctl enable --now portal-watcher
228
+```
229
+
230
+Adjust `User` to match your environment. Ensure the user belongs to the `docker` group:
231
+
232
+```bash
233
+sudo usermod -aG docker opc
234
+```
235
+
236
+### 5.4 Verify and monitor
237
+
238
+```bash
239
+# Service status
240
+sudo systemctl status portal-watcher
241
+
242
+# Live logs
243
+sudo journalctl -u portal-watcher -f
244
+
245
+# Today's logs only
246
+sudo journalctl -u portal-watcher --since today
247
+```
248
+
249
+## 6. Troubleshooting
250
+
251
+### 6.1 Ports blocked
252
253
Required inbound ports:
254
docs/examples/nginx-proxy/deploy_portal.sh
new
+7
@@ -0,0 +1,7 @@
1
+#!/bin/bash
2
+set -e
3
+
4
+docker pull ghcr.io/gosuda/portal:latest
5
+docker compose down portal
6
+docker compose up -d portal
7
+bash nginx_deploy.sh
docs/examples/nginx-proxy/nginx_deploy.sh
new
+9
@@ -0,0 +1,9 @@
1
+#!/bin/bash
2
+set -e
3
+
4
+docker cp nginx:/run/nginx.pid ./nginx.pid
5
+docker cp ./nginx.conf nginx:/etc/nginx/nginx.conf
6
+docker exec nginx nginx -s reload
7
+
8
+docker cp ./nginx.pid nginx:/run/nginx.pid
9
+rm ./nginx.pid
docs/examples/nginx-proxy/watch_and_deploy.sh
new
+40
@@ -0,0 +1,40 @@
1
+#!/usr/bin/env bash
2
+set -euo pipefail
3
+
4
+IMAGE="ghcr.io/gosuda/portal:latest"
5
+DIGEST_FILE="${DIGEST_FILE:-.portal_image_digest}"
6
+INTERVAL="${INTERVAL:-60}"
7
+DEPLOY_SCRIPT="${DEPLOY_SCRIPT:-deploy_portal.sh}"
8
+
9
+get_remote_digest() {
10
+ docker manifest inspect "$IMAGE" 2>/dev/null \
11
+ | grep -m1 '"digest"' \
12
+ | awk -F'"' '{print $4}'
13
+}
14
+
15
+echo "Watching $IMAGE for digest changes (interval: ${INTERVAL}s)"
16
+echo "Deploy script: $DEPLOY_SCRIPT"
17
+
18
+while true; do
19
+ NEW_DIGEST=$(get_remote_digest)
20
+
21
+ if [[ -z "$NEW_DIGEST" ]]; then
22
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] Failed to fetch digest, retrying in ${INTERVAL}s"
23
+ sleep "$INTERVAL"
24
+ continue
25
+ fi
26
+
27
+ OLD_DIGEST=""
28
+ if [[ -f "$DIGEST_FILE" ]]; then
29
+ OLD_DIGEST=$(cat "$DIGEST_FILE")
30
+ fi
31
+
32
+ if [[ "$NEW_DIGEST" != "$OLD_DIGEST" ]]; then
33
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] Digest changed: ${OLD_DIGEST:-<none>} -> $NEW_DIGEST"
34
+ echo "$NEW_DIGEST" > "$DIGEST_FILE"
35
+ bash "$DEPLOY_SCRIPT"
36
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] Deploy completed"
37
+ fi
38
+
39
+ sleep "$INTERVAL"
40
+done