#!/bin/bash # # ATCR UpCloud Initialization Script for Rocky Linux # # This script sets up ATCR on a fresh Rocky Linux instance. # Paste this into UpCloud's "User data" field when creating a server. # # What it does: # - Updates system packages # - Creates 2GB swap file (for 1GB RAM instances) # - Installs Docker and Docker Compose # - Creates directory structure # - Clones ATCR repository # - Creates systemd service for auto-start # - Builds and starts containers # # Post-deployment: # 1. Edit /opt/atcr/.env with your configuration # 2. Run: systemctl restart atcr # 3. Check logs: docker logs atcr-hold (for OAuth URL) # 4. Complete hold registration via OAuth set -euo pipefail # Configuration ATCR_DIR="/opt/atcr" ATCR_REPO="https://tangled.org/@evan.jarrett.net/at-container-registry" # UPDATE THIS ATCR_BRANCH="main" # Simple logging without colors (for cloud-init log compatibility) log_info() { echo "[INFO] $1" } log_warn() { echo "[WARN] $1" } log_error() { echo "[ERROR] $1" } # Function to check if command exists command_exists() { command -v "$1" >/dev/null 2>&1 } log_info "Starting ATCR deployment on Rocky Linux..." # Update system packages log_info "Updating system packages..." dnf update -y # Install required packages log_info "Installing prerequisites..." dnf install -y \ git \ wget \ curl \ nano \ vim log_info "Required ports: HTTP (80), HTTPS (443), SSH (22)" # Create swap file for instances with limited RAM if [ ! -f /swapfile ]; then log_info "Creating 2GB swap file (allows builds on 1GB RAM instances)..." dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress chmod 600 /swapfile mkswap /swapfile swapon /swapfile # Make swap permanent echo '/swapfile none swap sw 0 0' >> /etc/fstab log_info "Swap file created and enabled" free -h else log_info "Swap file already exists" fi # Install Docker if ! command_exists docker; then log_info "Installing Docker..." # Add Docker repository dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # Install Docker dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # Start and enable Docker systemctl enable --now docker log_info "Docker installed successfully" else log_info "Docker already installed" fi # Verify Docker Compose if ! docker compose version >/dev/null 2>&1; then log_error "Docker Compose plugin not found. Please install manually." exit 1 fi log_info "Docker Compose version: $(docker compose version)" # Create ATCR directory log_info "Creating ATCR directory: $ATCR_DIR" mkdir -p "$ATCR_DIR" cd "$ATCR_DIR" # Clone repository or create minimal structure if [ -n "$ATCR_REPO" ] && [ "$ATCR_REPO" != "https://tangled.org/@evan.jarrett.net/at-container-registry" ]; then log_info "Cloning ATCR repository..." git clone -b "$ATCR_BRANCH" "$ATCR_REPO" . else log_warn "ATCR_REPO not configured. You'll need to manually copy files to $ATCR_DIR" log_warn "Required files:" log_warn " - deploy/docker-compose.prod.yml" log_warn " - deploy/.env.prod.template" log_warn " - Dockerfile.appview" log_warn " - Dockerfile.hold" fi # Create .env file from template if it doesn't exist if [ -f "deploy/.env.prod.template" ] && [ ! -f "$ATCR_DIR/.env" ]; then log_info "Creating .env file from template..." cp deploy/.env.prod.template "$ATCR_DIR/.env" log_warn "IMPORTANT: Edit $ATCR_DIR/.env with your configuration!" fi # Create systemd services (caddy, appview, hold) log_info "Creating systemd services..." # Caddy service (reverse proxy for both appview and hold) cat > /etc/systemd/system/atcr-caddy.service <<'EOF' [Unit] Description=ATCR Caddy Reverse Proxy Requires=docker.service After=docker.service network-online.target Wants=network-online.target [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=/opt/atcr EnvironmentFile=/opt/atcr/.env # Start caddy container ExecStart=/usr/bin/docker compose -f /opt/atcr/deploy/docker-compose.prod.yml up -d caddy # Stop caddy container ExecStop=/usr/bin/docker compose -f /opt/atcr/deploy/docker-compose.prod.yml stop caddy # Restart caddy container ExecReload=/usr/bin/docker compose -f /opt/atcr/deploy/docker-compose.prod.yml restart caddy # Always restart on failure Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF # AppView service (registry + web UI) cat > /etc/systemd/system/atcr-appview.service <<'EOF' [Unit] Description=ATCR AppView (Registry + Web UI) Requires=docker.service atcr-caddy.service After=docker.service network-online.target atcr-caddy.service Wants=network-online.target [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=/opt/atcr EnvironmentFile=/opt/atcr/.env # Start appview container ExecStart=/usr/bin/docker compose -f /opt/atcr/deploy/docker-compose.prod.yml up -d atcr-appview # Stop appview container ExecStop=/usr/bin/docker compose -f /opt/atcr/deploy/docker-compose.prod.yml stop atcr-appview # Restart appview container ExecReload=/usr/bin/docker compose -f /opt/atcr/deploy/docker-compose.prod.yml restart atcr-appview # Always restart on failure Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF # Hold service (storage backend) cat > /etc/systemd/system/atcr-hold.service <<'EOF' [Unit] Description=ATCR Hold (Storage Service) Requires=docker.service atcr-caddy.service After=docker.service network-online.target atcr-caddy.service Wants=network-online.target [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=/opt/atcr EnvironmentFile=/opt/atcr/.env # Start hold container ExecStart=/usr/bin/docker compose -f /opt/atcr/deploy/docker-compose.prod.yml up -d atcr-hold # Stop hold container ExecStop=/usr/bin/docker compose -f /opt/atcr/deploy/docker-compose.prod.yml stop atcr-hold # Restart hold container ExecReload=/usr/bin/docker compose -f /opt/atcr/deploy/docker-compose.prod.yml restart atcr-hold # Always restart on failure Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target EOF # Reload systemd log_info "Reloading systemd daemon..." systemctl daemon-reload # Enable all services (but don't start yet - user needs to configure .env) systemctl enable atcr-caddy.service systemctl enable atcr-appview.service systemctl enable atcr-hold.service log_info "Systemd services created and enabled" # Create helper scripts log_info "Creating helper scripts..." # Script to rebuild and restart cat > "$ATCR_DIR/rebuild.sh" <<'EOF' #!/bin/bash set -e cd /opt/atcr docker compose -f deploy/docker-compose.prod.yml build docker compose -f deploy/docker-compose.prod.yml up -d docker compose -f deploy/docker-compose.prod.yml logs -f EOF chmod +x "$ATCR_DIR/rebuild.sh" # Script to view logs cat > "$ATCR_DIR/logs.sh" <<'EOF' #!/bin/bash cd /opt/atcr docker compose -f deploy/docker-compose.prod.yml logs -f "$@" EOF chmod +x "$ATCR_DIR/logs.sh" log_info "Helper scripts created in $ATCR_DIR" # Print completion message cat <<'EOF' ================================================================================ ATCR Installation Complete! ================================================================================ NEXT STEPS: 1. Configure environment variables: nano /opt/atcr/.env Required settings: - AWS_ACCESS_KEY_ID (UpCloud S3 credentials) - AWS_SECRET_ACCESS_KEY Pre-configured (verify these are correct): - APPVIEW_DOMAIN=atcr.io - HOLD_DOMAIN=hold01.atcr.io - HOLD_OWNER=did:plc:pddp4xt5lgnv2qsegbzzs4xg - S3_BUCKET=atcr - S3_ENDPOINT=https://blobs.atcr.io 2. Configure UpCloud Cloud Firewall (in control panel): Allow: TCP 22 (SSH) Allow: TCP 80 (HTTP) Allow: TCP 443 (HTTPS) Drop: Everything else 3. Configure DNS (Cloudflare - DNS-only mode): EOF echo " A atcr.io → $(curl -s ifconfig.me || echo '[server-ip]') (gray cloud)" echo " A hold01.atcr.io → $(curl -s ifconfig.me || echo '[server-ip]') (gray cloud)" echo " CNAME blobs.atcr.io → atcr.us-chi1.upcloudobjects.com (gray cloud)" cat <<'EOF' 4. Start ATCR services: systemctl start atcr-caddy atcr-appview atcr-hold 5. Check status: systemctl status atcr-caddy systemctl status atcr-appview systemctl status atcr-hold docker ps /opt/atcr/logs.sh Helper Scripts: /opt/atcr/rebuild.sh - Rebuild and restart containers /opt/atcr/logs.sh [service] - View logs (e.g., logs.sh atcr-hold) Service Management: systemctl start atcr-caddy - Start Caddy reverse proxy systemctl start atcr-appview - Start AppView (registry + UI) systemctl start atcr-hold - Start Hold (storage service) systemctl stop atcr-appview - Stop AppView only systemctl stop atcr-hold - Stop Hold only systemctl stop atcr-caddy - Stop all (stops reverse proxy) systemctl restart atcr-appview - Restart AppView systemctl restart atcr-hold - Restart Hold systemctl status atcr-caddy - Check Caddy status systemctl status atcr-appview - Check AppView status systemctl status atcr-hold - Check Hold status Documentation: https://tangled.org/@evan.jarrett.net/at-container-registry IMPORTANT: - Edit /opt/atcr/.env with S3 credentials before starting! - Configure UpCloud cloud firewall (see step 2) - DNS must be configured and propagated - Cloudflare proxy must be DISABLED (gray cloud) - Complete hold OAuth registration before first push EOF log_info "Installation complete. Follow the next steps above."