ghost: migrated to v6 + Docker Compose setup
This commit is contained in:
16
blog.knravish.me.conf
Normal file
16
blog.knravish.me.conf
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
server {
|
||||||
|
server_name blog.knravish.me;
|
||||||
|
index index.html index.htm;
|
||||||
|
|
||||||
|
include /etc/nginx/snippets/authelia-location.conf;
|
||||||
|
|
||||||
|
set $upstream http://127.0.0.1:2368;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
include /etc/nginx/snippets/proxy.conf;
|
||||||
|
include /etc/nginx/snippets/authelia-authrequest.conf;
|
||||||
|
proxy_pass $upstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
listen 80;
|
||||||
|
}
|
||||||
@@ -8,29 +8,30 @@ logFile=${HOME}/backup_logs/$(date +%y_%m).log
|
|||||||
{
|
{
|
||||||
echo -e "\n[+] ghost backup\n"
|
echo -e "\n[+] ghost backup\n"
|
||||||
|
|
||||||
cd "${BLOG_PATH}" || exit
|
TMP_DIR="/tmp/${USER}-backup"
|
||||||
|
mkdir -p "${TMP_DIR}"
|
||||||
|
|
||||||
if ! /usr/bin/expect "${HOME}"/"${USER}"-credentials.exp; then
|
sudo docker compose -f "${HOME}"/"${USER}"-compose.yaml stop ghost
|
||||||
curl -Ss \
|
|
||||||
-H "Title: Ghost Blog" \
|
|
||||||
-H "Priority: 3" \
|
|
||||||
-H "Tags: warning,backup" \
|
|
||||||
-d "Backup not completed - ghost backup failure" \
|
|
||||||
"${NOTIF_URL}"
|
|
||||||
rm -r "${BLOG_PATH}"/backup*
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "[+] local backup taken"
|
# Dump database
|
||||||
|
# shellcheck disable=SC2024
|
||||||
|
sudo docker exec ghost-mysql mysqldump \
|
||||||
|
-u root --password="${DB_ROOT_PASSWORD}" \
|
||||||
|
--no-tablespaces ghost >"${TMP_DIR}/db.sql"
|
||||||
|
|
||||||
if ! rclone copyto "${BLOG_PATH}"/backup*.zip "${BUCKET_PATH}" -v; then
|
# Backup content (exclude logs)
|
||||||
|
rsync -a --exclude='logs/' "${VOLUME_PATH}/content/" "${TMP_DIR}/content/"
|
||||||
|
|
||||||
|
sudo docker compose -f "${HOME}"/"${USER}"-compose.yaml start ghost
|
||||||
|
|
||||||
|
if ! rclone copy "${TMP_DIR}" "${BUCKET_PATH}" -v; then
|
||||||
curl -Ss \
|
curl -Ss \
|
||||||
-H "Title: Ghost Blog" \
|
-H "Title: Ghost Blog" \
|
||||||
-H "Priority: 3" \
|
-H "Priority: 3" \
|
||||||
-H "Tags: warning,backup" \
|
-H "Tags: warning,backup" \
|
||||||
-d "Backup not completed - rclone failure" \
|
-d "Backup not completed - rclone failure" \
|
||||||
"${NOTIF_URL}"
|
"${NOTIF_URL}"
|
||||||
rm -r "${BLOG_PATH}"/backup*
|
rm -rf "${TMP_DIR}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -40,6 +41,6 @@ logFile=${HOME}/backup_logs/$(date +%y_%m).log
|
|||||||
-H "Tags: heavy_check_mark,backup" \
|
-H "Tags: heavy_check_mark,backup" \
|
||||||
-d "Backup completed" \
|
-d "Backup completed" \
|
||||||
"${NOTIF_URL}"
|
"${NOTIF_URL}"
|
||||||
rm -r "${BLOG_PATH}"/backup*
|
rm -rf "${TMP_DIR}"
|
||||||
|
|
||||||
} &>>"$logFile"
|
} &>>"$logFile"
|
||||||
|
|||||||
42
ghost_server-compose_template.yaml
Normal file
42
ghost_server-compose_template.yaml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
services:
|
||||||
|
ghost:
|
||||||
|
image: ghost:6
|
||||||
|
container_name: ghost
|
||||||
|
pull_policy: always
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:${PORT}:2368
|
||||||
|
environment:
|
||||||
|
- url=${BASE_URL}
|
||||||
|
- database__client=mysql
|
||||||
|
- database__connection__host=db
|
||||||
|
- database__connection__user=ghost
|
||||||
|
- database__connection__password=${DB_PASSWORD}
|
||||||
|
- database__connection__database=ghost
|
||||||
|
- mail__transport=Direct
|
||||||
|
- logging__transports=["stdout"]
|
||||||
|
volumes:
|
||||||
|
- ${VOLUME_PATH}/content:/var/lib/ghost/content
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: mysql:8
|
||||||
|
container_name: ghost-mysql
|
||||||
|
pull_policy: always
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD}
|
||||||
|
- MYSQL_USER=ghost
|
||||||
|
- MYSQL_PASSWORD=${DB_PASSWORD}
|
||||||
|
- MYSQL_DATABASE=ghost
|
||||||
|
volumes:
|
||||||
|
- ${VOLUME_PATH}/mysql:/var/lib/mysql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "--password=${DB_ROOT_PASSWORD}"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 10
|
||||||
|
start_period: 30s
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"url": "https://blog.knravish.me",
|
|
||||||
"server": {
|
|
||||||
"port": 2368,
|
|
||||||
"host": "127.0.0.1"
|
|
||||||
},
|
|
||||||
"database": {
|
|
||||||
"client": "mysql",
|
|
||||||
"connection": {
|
|
||||||
"host": "postgres_hostname",
|
|
||||||
"user": "postgres_username",
|
|
||||||
"password": "postgres_password",
|
|
||||||
"database": "defaultdb",
|
|
||||||
"port": <postgres_port>,
|
|
||||||
"ssl": {
|
|
||||||
"ca": "<postgres_ssl_ca_cert_rsa_contents>",
|
|
||||||
"rejectUnauthorized": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mail": {
|
|
||||||
"transport": "Direct"
|
|
||||||
},
|
|
||||||
"logging": {
|
|
||||||
"transports": [
|
|
||||||
"file",
|
|
||||||
"stdout"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"process": "systemd",
|
|
||||||
"paths": {
|
|
||||||
"contentPath": "/var/www/blog.knravish.me/content"
|
|
||||||
},
|
|
||||||
"bootstrap-socket": {
|
|
||||||
"port": 8000,
|
|
||||||
"host": "localhost"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#!/usr/bin/expect
|
|
||||||
|
|
||||||
set stafftoken "<staff_token>"
|
|
||||||
|
|
||||||
spawn ghost backup
|
|
||||||
|
|
||||||
expect "Ghost Staff access token"
|
|
||||||
send "$stafftoken\r"
|
|
||||||
|
|
||||||
expect eof
|
|
||||||
@@ -1 +1,2 @@
|
|||||||
3 10 * * * /home/ghost_server/ghost_server-backup
|
3 10 * * * /home/ghost_server/ghost_server-backup
|
||||||
|
3 11 * * 2 /home/ghost_server/ghost_server-update
|
||||||
|
|||||||
@@ -1,52 +1,63 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# Sets up Ghost (Docker-based) from a backup stored in B2.
|
||||||
|
# Assumes B2 at ${BUCKET_PATH} contains:
|
||||||
|
# db.sql — mysqldump of the ghost database
|
||||||
|
# content/ — Ghost content directory
|
||||||
|
#
|
||||||
|
# Run ghost_server-migrate first to populate B2, then run this.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# shellcheck source=ghost_server-env
|
# shellcheck source=ghost_server-env
|
||||||
. "${HOME}"/"${USER}"-env
|
. "${HOME}"/"${USER}"-env
|
||||||
|
|
||||||
email_address=hello@knravish.me
|
|
||||||
echo -e "\n[+] setting up ghost\n\n-------\n"
|
echo -e "\n[+] setting up ghost\n\n-------\n"
|
||||||
|
|
||||||
echo "[+] node and companions"
|
TMP_DIR="/tmp/${USER}-restore"
|
||||||
# ghost doesn't play well with nvm for some reason, probably because of installation location and sudo access
|
|
||||||
# Download and import the Nodesource GPG key
|
|
||||||
sudo mkdir -p /etc/apt/keyrings
|
|
||||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
|
||||||
NODE_MAJOR=20 # Use a supported version
|
|
||||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install nodejs -y
|
|
||||||
|
|
||||||
echo "[+] getting ready..."
|
mkdir -p "${VOLUME_PATH}"/{content,mysql}
|
||||||
|
mkdir -p "${TMP_DIR}"
|
||||||
|
|
||||||
ghost_cli_ver="1.28.4"
|
envsubst <"${HOME}"/"${USER}"-compose_template.yaml >"${HOME}"/"${USER}"-compose.yaml
|
||||||
sudo npm i -g ghost-cli@${ghost_cli_ver}
|
|
||||||
|
|
||||||
sudo mkdir -p "${BLOG_PATH}"
|
# ---------------------------------------------------------------------------
|
||||||
sudo chown "${USER}":"${USER}" "${BLOG_PATH}"
|
# 1. Start MySQL; Ghost stays down until data is restored
|
||||||
sudo chmod 775 "${BLOG_PATH}"
|
# ---------------------------------------------------------------------------
|
||||||
|
echo "[+] starting MySQL..."
|
||||||
|
sudo docker compose -f "${HOME}"/"${USER}"-compose.yaml up -d db
|
||||||
|
|
||||||
# ghost really needs to update to newer nginx versions and conventions...
|
echo "[+] waiting for MySQL to be healthy..."
|
||||||
sudo mkdir -p /etc/nginx/sites-available/ /etc/nginx/sites-enabled/ /etc/nginx/snippets/
|
until sudo docker exec ghost-mysql mysqladmin ping -h localhost -u root --password="${DB_ROOT_PASSWORD}" --silent 2>/dev/null; do
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
|
|
||||||
echo "[+] ooh, interactive stuff"
|
# ---------------------------------------------------------------------------
|
||||||
|
# 2. Restore database from B2
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo "[+] downloading database dump from B2..."
|
||||||
|
rclone copyto "${BUCKET_PATH}/db.sql" "${TMP_DIR}/db.sql"
|
||||||
|
|
||||||
# currently track manually, maybe automate
|
echo "[+] restoring database..."
|
||||||
ghost_ver="5.130.6"
|
cat "${TMP_DIR}/db.sql" | sudo docker exec -i ghost-mysql mysql -u root --password="${DB_ROOT_PASSWORD}" ghost
|
||||||
|
|
||||||
cd "${BLOG_PATH}" && ghost install ${ghost_ver} --no-setup
|
echo "[+] database restored"
|
||||||
|
|
||||||
sudo cp "${HOME}"/"${USER}"-config.production.json "${BLOG_PATH}"/
|
# ---------------------------------------------------------------------------
|
||||||
sudo chown "${USER}":"${USER}" "${BLOG_PATH}"/"${USER}"-config.production.json
|
# 3. Restore content directory from B2
|
||||||
mv "${BLOG_PATH}"/"${USER}"-config.production.json "${BLOG_PATH}"/config.production.json
|
# ---------------------------------------------------------------------------
|
||||||
|
echo "[+] downloading content from B2..."
|
||||||
|
rclone copy "${BUCKET_PATH}/content/" "${VOLUME_PATH}/content/"
|
||||||
|
|
||||||
cd "${BLOG_PATH}" && ghost setup --auto --sslemail ${email_address}
|
echo "[+] content restored"
|
||||||
|
|
||||||
echo "[+] restoring backup data"
|
# ---------------------------------------------------------------------------
|
||||||
|
# 4. Start Ghost — it will run schema migrations (V5 -> V6) automatically
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
echo "[+] starting Ghost..."
|
||||||
|
sudo docker compose -f "${HOME}"/"${USER}"-compose.yaml up -d ghost
|
||||||
|
|
||||||
sudo rm -r "${BLOG_PATH}"/content/*
|
rm -rf "${TMP_DIR}"
|
||||||
|
|
||||||
rclone copyto "${BUCKET_PATH}" "${BLOG_PATH}"/ghostBackup.zip
|
echo -e "\n-----\nINFO\n-----"
|
||||||
sudo unzip "${BLOG_PATH}"/ghostBackup.zip -d "${BLOG_PATH}"/content/
|
echo "[i] Ghost is starting and running database migrations. This may take a minute."
|
||||||
sudo chown -R ghost:ghost "${BLOG_PATH}"/content/
|
echo "[i] Check logs: sudo docker compose -f ~/ghost_server-compose.yaml logs -f ghost"
|
||||||
|
|
||||||
echo -e "\n-----\nIMPORTANT\n-----\n[X] modify the nginx default config file to include the sites-enabled directory\n"
|
|
||||||
|
|||||||
14
ghost_server-teardown
Normal file
14
ghost_server-teardown
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
username=ghost_server
|
||||||
|
|
||||||
|
# application
|
||||||
|
sudo docker compose -f /home/${username}/${username}-compose.yaml down -v
|
||||||
|
|
||||||
|
uid_num=$(id -u $username)
|
||||||
|
sudo killall -9 -v -g -u $username
|
||||||
|
sudo crontab -r -u $username
|
||||||
|
sudo deluser --remove-all-files $username
|
||||||
|
|
||||||
|
# clean-up
|
||||||
|
sudo find / -user "$uid_num" -delete
|
||||||
11
ghost_server-update
Normal file
11
ghost_server-update
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
mkdir -p "${HOME}"/update_logs
|
||||||
|
logFile=${HOME}/update_logs/$(date +%y_%m).log
|
||||||
|
{
|
||||||
|
echo -e "\n[+] updating ghost\n"
|
||||||
|
|
||||||
|
sudo docker compose -f "${HOME}"/"${USER}"-compose.yaml pull &&
|
||||||
|
sudo docker compose -f "${HOME}"/"${USER}"-compose.yaml up -d --always-recreate-deps --remove-orphans &&
|
||||||
|
yes | sudo docker image prune -af
|
||||||
|
} &>>"$logFile"
|
||||||
Reference in New Issue
Block a user