-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathupdate.sh
More file actions
209 lines (189 loc) · 9.18 KB
/
Copy pathupdate.sh
File metadata and controls
209 lines (189 loc) · 9.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env bash
# CoolifyGo — updater
#
# Pulls the latest image from ghcr.io anonymously, recreates the container,
# and verifies health. All deployed app containers keep running during the
# update (Docker live-restore).
#
# Usage:
# curl -fsSL https://raw.githubusercontent.com/annihilatorrrr/gocoolify/main/update.sh | sudo bash
# sudo bash update.sh [--skip-apt] [--cleanup-api]
#
# Flags:
# --skip-apt Skip system package updates
# --cleanup-api Trigger Docker cleanup via the API after update
#
# Rollback if something goes wrong:
# docker stop coolifygo && docker rm coolifygo
# docker run -d --name coolifygo --restart no \
# --network host \
# -v /var/run:/var/run \
# -v /data/coolifygo:/data/coolifygo \
# -e COOLIFY_DATA_DIR=/data/coolifygo \
# -e SERVER_PORT=3000 \
# ghcr.io/annihilatorrrr/coolifygo:bak
# systemctl restart coolifygo
#
# NEVER run these manually on a coolifygo server:
# docker image prune -a ← wipes app images if containers are momentarily stopped
# docker system prune -a ← same + removes volumes
# Use Settings → Docker Cleanup in the dashboard instead.
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; BOLD='\033[1m'; NC='\033[0m'
info() { echo -e "${BLUE}[info]${NC} $*"; }
success() { echo -e "${GREEN}[ok]${NC} $*"; }
warn() { echo -e "${YELLOW}[warn]${NC} $*"; }
die() { echo -e "${RED}[error]${NC} $*" >&2; exit 1; }
# Detect the host's externally-reachable IPv4 — mirrors internal/netutil.OutboundIP
# so the final "Dashboard:" URL matches what the binary itself prints. `hostname -I`
# alone returns the first private VNIC IP on cloud VMs that NAT the public address
# (Oracle Cloud / AWS / GCP), giving the user a URL that doesn't work from outside
# the VPC. Order: PUBLIC_IP env → local outbound IP → echo service when local is
# RFC1918/CGNAT/link-local → fallback.
detect_public_ip() {
if [ -n "${PUBLIC_IP:-}" ]; then printf '%s' "$PUBLIC_IP"; return; fi
local local_ip=""
if command -v ip >/dev/null 2>&1; then
local_ip=$(ip -4 route get 1.1.1.1 2>/dev/null | awk 'NR==1 {for(i=1;i<=NF;i++) if ($i=="src") {print $(i+1); exit}}')
fi
if [ -z "$local_ip" ] && command -v hostname >/dev/null 2>&1; then
local_ip=$(hostname -I 2>/dev/null | awk '{print $1}')
fi
local is_private=0
case "$local_ip" in
10.*|192.168.*|169.254.*) is_private=1 ;;
172.1[6-9].*|172.2[0-9].*|172.3[01].*) is_private=1 ;;
100.6[4-9].*|100.[7-9][0-9].*|100.1[01][0-9].*|100.12[0-7].*) is_private=1 ;;
esac
if [ "$is_private" = "1" ]; then
local url pub
for url in https://api.ipify.org https://ifconfig.me/ip https://icanhazip.com; do
pub=$(curl -sf --max-time 2 "$url" 2>/dev/null | tr -d '[:space:]')
if printf '%s' "$pub" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then
printf '%s' "$pub"; return
fi
done
fi
printf '%s' "${local_ip:-localhost}"
}
[[ "$EUID" -ne 0 ]] && die "Run as root: sudo bash update.sh"
[ "$(uname -s)" != "Linux" ] && die "Only Linux is supported."
COOLIFY_DIR="${COOLIFY_DIR:-/data/coolifygo}"
COOLIFY_PORT="${COOLIFY_PORT:-3000}"
# Repo source. Edit the default below or pass COOLIFY_REPO=you/yourfork at runtime.
REPO="${COOLIFY_REPO:-annihilatorrrr/coolifygo}"
REPO_RAW="https://raw.githubusercontent.com/annihilatorrrr/gocoolify/main"
IMAGE="ghcr.io/${REPO}:latest"
IMAGE_BAK="ghcr.io/${REPO}:bak"
SKIP_APT=0
CLEANUP_API=0
for arg in "$@"; do
case "$arg" in
--skip-apt) SKIP_APT=1 ;;
--cleanup-api) CLEANUP_API=1 ;;
--help|-h)
echo "Usage: update.sh [--skip-apt] [--cleanup-api]"
echo ""
echo " --skip-apt Skip apt system package updates"
echo " --cleanup-api Trigger Docker cleanup via API after update"
echo " (requires COOLIFY_API_TOKEN env var)"
exit 0 ;;
*) die "Unknown option: $arg" ;;
esac
done
SERVICE="/etc/systemd/system/coolifygo.service"
[ ! -f "$SERVICE" ] && die "CoolifyGo is not installed. Run: curl -fsSL ${REPO_RAW}/pscripts/install.sh | sudo bash"
[ ! -d "$COOLIFY_DIR" ] && die "Data directory $COOLIFY_DIR not found."
# Read port from running container env if available, fall back to default.
RUNNING_PORT=$(docker inspect coolifygo --format '{{range .Config.Env}}{{println .}}{{end}}' 2>/dev/null \
| grep '^SERVER_PORT=' | cut -d= -f2 || echo "")
PORT="${RUNNING_PORT:-$COOLIFY_PORT}"
echo ""
echo -e "${BOLD}CoolifyGo — Update${NC}"
echo ""
info "Image : $IMAGE"
info "Port : $PORT"
echo ""
# ─── Step 1: Tag current image as backup, then pull new image ─────────────────
OLD_IMAGE_ID=$(docker inspect coolifygo --format '{{.Image}}' 2>/dev/null || echo "")
if [ -n "$OLD_IMAGE_ID" ]; then
docker tag "$OLD_IMAGE_ID" "$IMAGE_BAK" 2>/dev/null || true
info "Previous image tagged as :bak for rollback"
fi
info "Pulling $IMAGE…"
docker pull "$IMAGE" || die "Failed to pull image. Check network connectivity to ghcr.io."
success "Image pulled"
# ─── Step 2: System packages (optional) ───────────────────────────────────────
if [ $SKIP_APT -eq 0 ] && command -v apt-get &>/dev/null; then
info "Updating system packages (apt)..."
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get upgrade -y --no-install-recommends \
-o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confold"
apt-get autoremove -y -qq
success "System packages updated"
info "Waiting for Docker daemon..."
for i in $(seq 1 15); do
docker info &>/dev/null && break
sleep 2
done
docker info &>/dev/null || die "Docker daemon not responding after apt upgrade"
success "Docker is ready"
else
[ $SKIP_APT -eq 1 ] && info "Skipping apt upgrade (--skip-apt)"
fi
# ─── Step 3: Stop old container, recreate with new image ──────────────────────
# Deployed app containers keep running: live-restore=true in daemon config.
info "Stopping CoolifyGo container..."
docker stop coolifygo 2>/dev/null || true
docker wait coolifygo 2>/dev/null || true
success "Container fully stopped (your deployed containers keep running)"
info "Removing old container..."
docker rm coolifygo 2>/dev/null || true
success "Old container removed"
info "Starting new container…"
docker run -d --name coolifygo --restart no --network host -v /var/run:/var/run -v "${COOLIFY_DIR}:/data/coolifygo" -e COOLIFY_DATA_DIR=/data/coolifygo -e SERVER_PORT="${PORT}" "${IMAGE}" || die "Failed to start new container. Rollback: docker run ... ${IMAGE_BAK}"
success "Container started"
# ─── Step 4: Health check ─────────────────────────────────────────────────────
info "Waiting for CoolifyGo to be healthy (port $PORT)..."
READY=0
for i in $(seq 1 30); do
if curl -sf "http://localhost:${PORT}/health" &>/dev/null; then
READY=1
break
fi
sleep 2
done
if [ $READY -eq 0 ]; then
warn "Health check timed out — check logs: docker logs coolifygo -f"
warn "Rollback: docker stop coolifygo && docker rm coolifygo && docker run -d --name coolifygo --restart no --network host -v /var/run:/var/run -v ${COOLIFY_DIR}:/data/coolifygo -e COOLIFY_DATA_DIR=/data/coolifygo -e SERVER_PORT=${PORT} ${IMAGE_BAK}"
fi
# ─── Step 5: API cleanup (optional) ───────────────────────────────────────────
if [ $CLEANUP_API -eq 1 ] && [ $READY -eq 1 ]; then
if [[ -n "${COOLIFY_API_TOKEN:-}" ]]; then
info "Running Docker cleanup via API..."
curl -sf -X POST "http://localhost:${PORT}/api/v1/internal/cleanup" \
-H "Authorization: Bearer $COOLIFY_API_TOKEN" >/dev/null \
&& success "Docker cleanup triggered" \
|| warn "Cleanup request failed — run it from Settings → Docker Cleanup"
else
warn "Set COOLIFY_API_TOKEN env var to use --cleanup-api, or use Settings → Docker Cleanup"
fi
fi
# ─── Done ─────────────────────────────────────────────────────────────────────
SERVER_IP=$(detect_public_ip)
echo ""
if [ $READY -eq 1 ]; then
echo -e "${GREEN}${BOLD}Update complete!${NC}"
else
echo -e "${YELLOW}${BOLD}Container recreated — still starting up.${NC}"
fi
echo ""
echo -e " Dashboard : http://${SERVER_IP}:${PORT}"
echo -e " Logs : docker logs coolifygo -f"
echo -e " Rollback : docker stop coolifygo && docker rm coolifygo && docker run -d --name coolifygo --restart no --network host -v /var/run:/var/run -v ${COOLIFY_DIR}:/data/coolifygo -e COOLIFY_DATA_DIR=/data/coolifygo -e SERVER_PORT=${PORT} ${IMAGE_BAK}"
echo ""
echo -e "${YELLOW}${BOLD}REMINDER:${NC} Never run 'docker image prune -a' directly on this server."
echo -e " Use Settings → Docker Cleanup in the dashboard instead."
echo ""