4.7 KB
raw
#!/bin/sh
#
# quickstart.sh
#
# Alpine Linux host provisioning. Run once on a fresh server.
# Push the entire hosts/alpine/ directory via scp first:
#
# scp -r hosts/alpine/ root@your-server:/root/alpine
# ssh root@your-server "cd /root/alpine && sh quickstart.sh"
#
set -e
# Install dependencies
apk update
apk upgrade
apk add \
neovim \
rsync \
git \
ip6tables \
iptables \
ufw \
restic \
docker \
docker-cli-buildx \
docker-compose
# Configure firewall
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 80/udp # http3
ufw allow 443/tcp
ufw allow 443/udp # http3
ufw --force enable
# Install configuration files
cp etc/apk/repositories /etc/apk/repositories && chmod 644 /etc/apk/repositories
cp etc/periodic/daily/apk-autoupgrade /etc/periodic/daily/apk-autoupgrade && chmod 700 /etc/periodic/daily/apk-autoupgrade
cp etc/periodic/daily/restic-autobackup /etc/periodic/daily/restic-autobackup && chmod 700 /etc/periodic/daily/restic-autobackup
cp root/server-health-check.sh /root/server-health-check.sh && chmod 700 /root/server-health-check.sh
cp root/restore.sh /root/restore.sh && chmod 700 /root/restore.sh
# Create directory structure
mkdir -p /srv/git /srv/docker /srv/data
mkdir -p /root/.restic && chmod 700 /root/.restic
echo ""
echo "** Place restic credentials before backups will run: **"
echo " /root/.restic/password restic repo password (chmod 600)"
echo " /root/.restic/b2-env exports B2_ACCOUNT_ID and B2_ACCOUNT_KEY (chmod 600)"
echo ""
# Provision each project from projects.conf
while IFS='|' read -r name repo branch has_data has_migrate; do
# Skip comments and blank lines
case "$name" in \#*|"") continue ;; esac
echo "--- Provisioning $name ---"
# Bare git repo for push-to-deploy
if [ ! -d "/srv/git/${name}.git" ]; then
git init --bare "/srv/git/${name}.git"
fi
# Working clone for docker-compose
if [ ! -d "/srv/docker/${name}" ]; then
git clone "git@github.com:${repo}.git" "/srv/docker/${name}" -b "$branch"
fi
# Data directory
if [ "$has_data" = "yes" ] && [ ! -d "/srv/data/${name}" ]; then
mkdir -p "/srv/data/${name}"
fi
# .env file from sample if it exists
if [ ! -f "/srv/docker/${name}/.env" ] && [ -f "/srv/docker/${name}/samplefiles/env.sample" ]; then
cp "/srv/docker/${name}/samplefiles/env.sample" "/srv/docker/${name}/.env"
echo " ** Review and edit /srv/docker/${name}/.env **"
fi
# Post-receive hook
cat > "/srv/git/${name}.git/hooks/post-receive" << HOOK
#!/bin/sh
while read oldrev newrev ref; do
if [ "\$ref" = "refs/heads/${branch}" ]; then
unset GIT_DIR
START_TIME=\$(date +%s)
cd /srv/docker/${name}
git pull
docker compose up --build --detach
# Reattach every container in the project to the shared edge network.
# Resolved via \`compose ps\` (not \`${name}\`) so this works regardless
# of the compose service name or container_name override.
for cid in \$(docker compose ps -q); do
docker network connect bythewood-edge "\$cid" 2>/dev/null || true
done
HOOK
if [ "$has_migrate" = "yes" ]; then
cat >> "/srv/git/${name}.git/hooks/post-receive" << 'MIGRATE'
docker compose exec -T web python3 manage.py migrate --noinput
MIGRATE
fi
cat >> "/srv/git/${name}.git/hooks/post-receive" << TAIL
docker container prune --force
docker image prune --force
END_TIME=\$(date +%s)
echo "Total build time: \$((END_TIME - START_TIME))s"
fi
done
TAIL
chmod +x "/srv/git/${name}.git/hooks/post-receive"
done < srv/projects.conf
# Start services and add to startup
rc-update add ufw boot && rc-service ufw start
rc-update add docker boot && rc-service docker start
# Shared edge network: Caddy and every project's web container attach here so
# Caddy can reverse_proxy by container name. Created idempotently; the
# post-receive hooks reattach project containers after each rebuild.
docker network create bythewood-edge 2>/dev/null || true
# Caddy runs as a container, not on the host. Bind-mount the Caddyfile so
# `docker compose exec caddy caddy reload` picks up edits without a rebuild.
# /srv/data/caddy holds ACME certs and account keys; backed up via restic
# along with every other /srv/data/<project>/ directory.
mkdir -p /srv/docker/caddy /srv/data/caddy
cp srv/caddy/docker-compose.yml /srv/docker/caddy/docker-compose.yml
cp srv/caddy/Caddyfile /srv/docker/caddy/Caddyfile
( cd /srv/docker/caddy && docker compose up --detach )
echo ""
echo "Server provisioned. Review .env files in /srv/docker/*/ before starting containers."
echo "Add server remotes to your local repos: git remote add server root@this-server:/srv/git/PROJECT.git"