# Runbook: Self-hosted Pilot ## Gaussian Wellworks ## 1. Runbook Objective This runbook provides a step-by-step procedure to deploy a secure, self-hosted pilot environment in DigitalOcean within five business days, including infrastructure provisioning, domain and DNS configuration, email platform integration, containerized Nextcloud deployment, and secure HTTPS access via reverse proxy. The objective is to deliver a fully functional, production-ready pilot system that supports internal collaboration and client file ingestion workflows, with validated end-to-end functionality (upload, access, delivery), clear administrative controls, and a scalable foundation for future expansion or migration. ## 2. Critical Dependencies ### 2.1 Business Domain Ownership ### 2.2 Administrative Access ### 2.3 Decision Checklist ## 3. Detailed Implementation Runbook ### 3.1 Create Digital Ocean Account **Objective** Provision a cloud account capable of hosting the pilot environment: - Account created and secured - Billing configured - Team/ownership clarified - Ready to provision Droplet (Task 3.3) **Architectural Context** This account will own: - Droplet (server) - Backups/snapshots - Networking (firewall, IP) - Future scaling resources **Step 1 - Register Account** Go to: ``` https://cloud.digitalocean.com/registrations/new ``` 1.1 Enter account details ``` Email address (prefer client-owned) Password (strong, unique) ``` 1.2 Verify email ``` Check inbox Click verification link ``` **Step 2 - Secure the Account (Highly Recommended)** 2.1 Enable Two-Factor Authentication (2FA) Navigate to: ``` Settings → Security → Two-Factor Authentication ``` Enable using: Authenticator app (recommended) 2.2 Store recovery codes ✔ Save securely (password manager) **Step 3 - Add Billing Method** Navigate to: ``` Billing → Payment Methods ``` 3.1 Add payment method 3.2 Confirm billing active **Step 4 - Create/Confirm Team Context** DigitalOcean uses teams/projects. Navigate: ``` Projects → Default Project ``` 4.1 Rename project (recommended) GaussianWellworks-Pilot 4.2 Assign resources later Droplets will be attached here. **Step 5 - Configure Default Settings** 5.1 Enable backups (account-level awareness) While backups are enabled per droplet, confirm understanding: ✔ Backups cost ~20% of droplet price ✔ Enable during droplet creation 5.2 (Optional) Enable monitoring Settings → Monitoring → Enable *Not required for pilot, but useful later.* **Validation Checklist** - [✔] Account created - [✔] Email verified - [✔] 2FA enabled - [✔] Billing method added - [✔] Project created/renamed - [✔] Dashboard accessible - [✔] Able to initiate droplet creation ### 3.2 Create SSH Key Pair (Windows 11) **Objective** Create an SSH key pair on a Windows 11 workstation and upload the public key to DigitalOcean so it can be attached when provisioning the Ubuntu droplet. DigitalOcean recommends SSH keys over passwords for Droplet access, and uploaded team keys can be selected during Droplet creation. **Generate key** ```Powershell Powershell ssh-keygen -t ed25519 -C "admin@company.com" ``` Accept default path: ``` C:\Users\\.ssh\id_ed25519 ``` **Copy public key** ```Powershell Powershell Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | Set-Clipboard ``` **Upload to DigitalOcean** - Settings → Security → SSH Keys → Add - Name: admin-windows11 **Validation Checklist** - [✔] Key visible in DigitalOcean ### 3.3 Provision Droplet **Objective** Provision a production-ready virtual server (Droplet) in DigitalOcean that will host: - Nextcloud (Docker-based) - Reverse proxy (Nginx) - Supporting services This step establishes the core infrastructure node for the entire pilot. **Create Dropet** 1. Login to DigitalOcean 2. Click Create 3. Select Droplets **Droplet Configuration** ``` Image: Ubuntu 22.04 LTS Plan: Premium Intel Size: 2 vCPU / 8GB RAM Region: closest to users Auth: SSH key Backups: Enabled Hostname: gw-drive-01 ``` **Test SSH** ```Powershell ssh root@ ``` **Validation Checklist** - [✔] SSH login works - [✔] IP recorded ### 3.4 Initial Server Configuration **Objective** Harden the freshly provisioned Ubuntu server and establish a secure administrative baseline before any application deployment. At the end of this task, the system will: - Allow SSH access only via key-based authentication - Use a non-root administrative user - Be fully patched and up to date - Be ready for firewall and application installation **Create admin user** ```Bash Bash adduser adminuser usermod -aG sudo adminuser ``` **Copy SSH key** ```Bash Bash rsync --archive --chown=adminuser:adminuser ~/.ssh /home/adminuser ``` **Disable root login** ```Bash Bash sudo tee /etc/ssh/sshd_config > /dev/null << 'EOF' PermitRootLogin no PasswordAuthentication no EOF ``` ```Bash Bash sudo systemctl restart ssh ``` **Test login** ```Powershell Powershell ssh adminuser@ ``` **Update system** ```Bash Bash sudo apt update sudo apt upgrade -y sudo apt autoremove -y ``` **Validation** [✔] adminuser login works [✔] root login blocked [✔] system updated ### 3.5 Configure Firewall **Objective** Establish a baseline network security posture so that: - Only required ports are exposed - All other inbound traffic is blocked - SSH access remains available - The system is prepared for web access (Nextcloud + HTTPS) **DigitalOcean Firewall** Allow: ``` 22 (SSH) → your IP 80 (HTTP) → all 443 (HTTPS) → all ``` Attach to droplet. **UFW** ```Bash Bash sudo apt install ufw -y sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow 22/tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable ``` **Validation** ```Bash Bash sudo ufw status ``` ### 3.6 Configure DNS **Objective** Create a public DNS record so that: ``` drive.company.com → your Droplet public IP ``` This enables: - Browser access to Nextcloud (Day 3) - SSL certificate issuance (Let’s Encrypt) - A stable, user-friendly endpoint **Create A record** ``` Host: drive Value: TTL: 300 ``` **Validate** ```Powershell Powershell nslookup drive.company.com ``` *Note* ⚠ DNS propagation required before SSL ### 3.7 Install Docker **Objective** Prepare the server to run containerized services by installing: - Docker Engine (container runtime) - Docker Compose (multi-container orchestration) **Required Dependencies** ```Bash Bash sudo apt install ca-certificates curl gnupg -y sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg ``` ```Bash Bash echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ``` **Install Docker** ```Bash Bash sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y ``` **Enable Docker** ```Bash Bash sudo systemctl enable docker sudo systemctl start docker sudo usermod -aG docker adminuser ``` Reconnect SSH. **Test** ```Bash Bash docker run hello-world ``` ### 3.10 Deploy Nextcloud **Objective** Deploy a production-capable Nextcloud stack using Docker Compose, including: - Nextcloud application container - MariaDB database container - Redis (for performance + file locking) - Persistent storage volumes **Create directory** ```Bash Bash mkdir -p ~/apps/nextcloud cd ~/apps/nextcloud ``` **Create .env** ```Bash Bash sudo tee .env > /dev/null << 'EOF' MYSQL_ROOT_PASSWORD=StrongRootPassword MYSQL_PASSWORD=StrongDBPassword MYSQL_DATABASE=nextcloud MYSQL_USER=nextcloud NEXTCLOUD_ADMIN_USER=admin NEXTCLOUD_ADMIN_PASSWORD=StrongAdminPassword MYSQL_HOST=db REDIS_HOST=redis NEXTCLOUD_TRUSTED_DOMAINS= EOF ``` **Create docker-compose** ```Bash Bash sudo tee docker-compose.yml > /dev/null << 'EOF' version: '3.9' services: db: image: mariadb:10.6 restart: always env_file: - .env volumes: - db_data:/var/lib/mysql redis: image: redis:7-alpine restart: always app: image: nextcloud:apache restart: always ports: - "8080:80" env_file: - .env depends_on: - db - redis volumes: - nextcloud_data:/var/www/html volumes: db_data: nextcloud_data: EOF ``` **Start** ```Bash Bash docker compose up -d ``` **Validate** ``` http://:8080 ``` ### 3.11 Validate Internal Access **Objective** Confirm that the deployed Nextcloud instance is: - Reachable over the network (via Droplet IP) - Functionally operational (login, file operations) - Persisting data correctly - Stable prior to exposing externally This is a hard validation gate—do not proceed until this passes. **Test:** - [ ] Login - [ ] Upload file - [ ] Create folder - [ ] Restart containers - [ ] Data persists ### 3.12 Install Nginx **Objective** Install and validate Nginx as the web server that will act as a reverse proxy in front of Nextcloud. At the end of this step: - Nginx is installed and running - Port 80 (HTTP) is actively serving traffic - Server is ready for reverse proxy configuration (Task 3.13) ```Bash Bash sudo apt install nginx -y sudo systemctl enable nginx sudo systemctl start nginx ``` **Test:** ``` http:// → nginx welcome page ``` ### 3.13 Configure HTTP Reverse Proxy (Staging Only) **Objective** Bind your domain to the application by configuring Nginx to proxy: ``` drive.company.com → Nginx (port 80) → Nextcloud (port 8080) ``` At the end of this step: - Nginx reverse proxy configuration staged - HTTP server block created for drive.company.com - Nginx config syntax validates - Nginx reloads successfully - ⚠ Browser access may redirect to HTTPS and show 404 until SSL/HTTPS configuration is completed ```Bash Bash sudo tee /etc/nginx/sites-available/nextcloud > /dev/null << 'EOF' server { listen 80; server_name drive.company.com; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto http; } } EOF ``` **Enable:** ```Bash Bash sudo ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` *Important* - ⚠ This step is NOT fully usable yet - ⚠ HTTPS may redirect and show 404 (expected) ### 3.14 Install SSL (Certbot) **Objective** Obtain and install a valid SSL certificate so that: https://drive.company.com is: - Trusted (no browser warnings) - Encrypted (HTTPS) - Routed through Nginx to Nextcloud ```Bash Bash sudo apt install certbot python3-certbot-nginx -y sudo certbot --nginx -d drive.company.com ``` **Validate** - ✔ Certificate issued - ✔ HTTPS reachable - ⚠ May still show 404 (expected) ### 3.15 Finalize HTTPS Reverse Proxy **Objective** Complete the system by: - Wiring HTTPS (port 443) to Nextcloud - Enforcing HTTP → HTTPS redirect - Applying required Nextcloud proxy settings - Performing end-to-end validation At completion: - https://drive.company.com fully functional - Secure (SSL) - Reverse proxy working - Upload/download validated **Replace config** ```Bash Bash sudo tee /etc/nginx/sites-available/nextcloud > /dev/null << 'EOF' server { listen 80; server_name drive.company.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name drive.company.com; ssl_certificate /etc/letsencrypt/live/drive.company.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/drive.company.com/privkey.pem; client_max_body_size 10G; location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; } } EOF ``` **Reload** ```Bash Bash sudo nginx -t sudo systemctl reload nginx ``` **Fix Nextcloud** ```Bash Bash docker exec -u www-data nextcloud-app php occ config:system:set trusted_domains 1 --value="drive.company.com" docker exec -u www-data nextcloud-app php occ config:system:set overwrite.cli.url --value="https://drive.company.com" docker exec -u www-data nextcloud-app php occ config:system:set overwriteprotocol --value="https" ``` **Final Validation** https://drive.company.com - ✔ Login works - ✔ Upload works - ✔ Download works - ✔ No warnings - ✔ HTTPS padlock present **Cleanup** ```Bash Bash sudo ufw delete allow 8080/tcp ``` ✅ Final Outcome - ✔ Secure Nextcloud deployment - ✔ HTTPS reverse proxy functional - ✔ Docker-based architecture - ✔ Ready for users and workflows ### 3.16 Create Email Provider Account (Fastmail) **Objective** Establish a business-grade email platform using Fastmail so that: - domain-based email (user@company.com) - reliable outbound mail (for Nextcloud notifications later) - foundation for SPF/DKIM/DMARC **Architectural Context** Email is not hosted on your droplet. Your infrastructure will later integrate with it for: - Nextcloud notifications - User accounts - Client communication **Prerequisites** - Domain ownership confirmed - Access to DNS registrar - Decision: Fastmail selected (per runbook) **Step 1 - Create Fastmail Account** Go to: https://www.fastmail.com 1.1 Sign up - Click Sign Up - Choose a plan (Standard is sufficient for pilot) - Enter: ``` Name Email (temporary Fastmail domain) Password ``` 1.2 Complete account creation You will land in the Fastmail admin interface. **Step 2 - Add Your Domain** 2.1 Navigate to Domains In Fastmail: ``` Settings → Domains → Add Domain ``` 2.2 Enter your domain ``` company.com ``` Click: ``` Add Domain ``` **Step 3 - Verify Domain Ownership** Fastmail will present a TXT verification record. 3.1 Copy TXT record Example: ``` Type: TXT Name: @ Value: fm-domain-verification=abc123xyz ``` 3.2 Add to DNS (Registrar) In your DNS provider (e.g., GoDaddy or Cloudflare): Add: ``` Type: TXT Host: @ Value: fm-domain-verification=abc123xyz TTL: 300 ``` 3.3 Wait for propagation ``` ~5–15 minutes typical, but may be as much as 24 hours ``` 3.4 Verify in Fastmail Click: ``` Verify Domain ``` Expected Result - Domain verified successfully **Step 4 - Set Domain as Default** In Fastmail: ``` Settings → Domains → company.com → Set as default ``` This ensures: - new users → user@company.com **Step 5 - Create Initial Admin Mailbox** 5.1 Navigate to Users ``` Settings → Users → Add User ``` 5.2 Create admin user Example: ``` Username: admin Email: admin@company.com Password: (strong password) ``` 5.3 Save Expected Result - Mailbox created - Can send/receive (after DNS setup in 3.8) **Step 6 - Basic Access Test (Pre-DNS)** Login via: ``` https://app.fastmail.com ``` Expected - Inbox accessible - Account functional (even before MX setup) - Email is NOT functional yet **Validation Checklist** - [✔] Fastmail account created - [✔] Domain added to Fastmail - [✔] TXT verification record created - [✔] Domain verified - [✔] Default domain set - [✔] Admin mailbox created - [✔] Able to login to Fastmail ### 3.17 Configure Email DNS **Objective** Make your domain fully capable of sending and receiving email by configuring DNS records required by Fastmail: - Incoming mail routing (MX) - Authorized senders (SPF) - Cryptographic signing (DKIM) - Policy & reporting (DMARC) *Changing MX records WILL impact live email.* If the client currently uses another provider: - Schedule a cutover window - Lower TTL beforehand (if possible) - Expect brief delivery delays during propagation **Prerequisites** - Domain added & verified in Fastmail (Task 3.7) - Access to DNS registrar - Admin mailbox created **Step 1 - Gather Fastmail DNS Records** In Fastmail: ``` Settings → Domains → company.com → DNS Settings ``` Fastmail will provide all required records. Use those exact values if they differ from below. **Step 2 - Configure MX Records (Mail Routing)** 2.1 Remove Existing MX Records ⚠️ Only do this at cutover time. 2.2 Add Fastmail MX Records ``` Type: MX Host: @ Priority: 10 Value: in1-smtp.messagingengine.com Type: MX Host: @ Priority: 20 Value: in2-smtp.messagingengine.com ``` **Step 3 - Configure SPF (Sender Authorization)** 3.1 Add SPF record ``` Type: TXT Host: @ Value: v=spf1 include:spf.messagingengine.com ~all TTL: 300 ``` Notes - Only ONE SPF record per domain - Merge with existing SPF if present **Step 4 - Configure DKIM (Email Signing)** 4.1 Get DKIM from Fastmail In Fastmail DNS settings, copy the DKIM record. Example: ``` Type: CNAME Host: fm1._domainkey Value: fm1.company.com.dkim.fmhosted.com Type: CNAME Host: fm2._domainkey Value: fm2.company.com.dkim.fmhosted.com Type: CNAME Host: fm3._domainkey Value: fm3.company.com.dkim.fmhosted.com 4.2 Add all DKIM records ``` These enable: - Email authenticity verification - Better deliverability **Step 5 - Configure DMARC (Policy & Reporting)** 5.1 Add DMARC record ``` Type: TXT Host: _dmarc Value: v=DMARC1; p=none; rua=mailto:admin@company.com TTL: 300 ``` Explanation - p=none → monitor only (safe for pilot) - rua= → aggregate reports sent to admin mailbox Future Hardening Later you can move to: - p=quarantine → soft enforcement - p=reject → full enforcement **Step 6 - Verify DNS Propagation** 6.1 Check MX nslookup -type=MX company.com Expected: in1-smtp.messagingengine.com in2-smtp.messagingengine.com 6.2 Check SPF nslookup -type=TXT company.com 6.3 Check DKIM nslookup -type=CNAME fm1._domainkey.company.com 6.4 Check DMARC nslookup -type=TXT _dmarc.company.com **Step 7 - Functional Email Test** 7.1 Send inbound test From Gmail/Outlook: ``` Send email to: admin@company.com ``` Expected: - Email received in Fastmail inbox 7.2 Send outbound test From Fastmail: ``` Send email to external address ``` Expected: - Email delivered - Not marked as spam **Step 8 - Validate Deliverability (Recommended)** Use: - mail-tester.com - mxtoolbox.com Expected - SPF pass - DKIM pass - DMARC pass - Low spam score **Step 9 - Propagation Expectations** - MX: 5–30 minutes typical - SPF: immediate to 1 hour - DKIM: 15–60 minutes - DMARC: 15–60 minutes Worst case: Up to 24 hours **Validation Checklist** - [✔] MX records updated - [✔] SPF record present - [✔] DKIM records configured (all 3) - [✔] DMARC record created - [✔] DNS resolves correctly - [✔] Email received externally - [✔] Email sent externally - [✔] Deliverability validated ### 3.18 Create Email Accounts **Objective** Provision internal user mailboxes in Fastmail so that: - Each employee has a company email (user@company.com) - Accounts align with Nextcloud users (identity consistency) - System ready for communication + notifications **Architectural Context** User identity is now split across: - Fastmail → Email identity (user@company.com) - Nextcloud → Application access (files, shares) **Step 1 - Define User List** Before creating accounts, confirm: | Name | Email Address | |-------|-----| | John Smith | jsmith@company.com | | Jane Doe | jdoe@company.com | | Operations | ops@company.com | Best Practices - Use simple, consistent naming (first initial + last name) - Avoid special characters - Reserve generic aliases (admin, info, support) **Step 2 - Access User Management** In Fastmail: ``` Settings → Users → Add User ``` **Step 3 - Create Individual User Accounts 3.1 Enter user details For each user: ``` Username: jsmith Email: jsmith@company.com Full Name: John Smith Password: (temporary strong password) ``` 3.2 Set password policy - Use strong temporary password - Require user to change on first login (recommended) 3.3 Save user Repeat for all internal users. **Step 4 - Assign Roles & Permissions** Default (Recommended for Pilot) - Standard User (no admin privileges) Admin Accounts Only assign admin rights to: - System owner - IT/consultant (if required) **Step 5 - Create Aliases (Optional but Recommended)** Common aliases ``` admin@company.com info@company.com support@company.com ``` How to create In Fastmail: ``` Settings → Domains → company.com → Aliases ``` Example support@company.com → jsmith@company.com **Step 6 - Validate Each Account** 6.1 Login test ``` https://app.fastmail.com ``` 6.2 Send test email - Send to another internal user - Send to external address (e.g., Gmail) 6.3 Receive test email - Receive from external sender - Confirm inbox delivery **Step 7 - Distribute Credentials** For each user, provide: - Email address - Temporary password - Login URL: https://app.fastmail.com - Instructions to change password **Validation Checklist** - [✔] All internal users created - [✔] Email addresses correct - [✔] Login verified for each user - [✔] Send/receive tested - [✔] Aliases created (if needed) - [✔] Credentials distributed securely - [✔] Usernames aligned with Nextcloud ### 3.19 Create Nextcloud Users **Objective** Provision user accounts in Nextcloud aligned with your email identities so that: - Each internal user has access to the platform - Identity is consistent across email and Nextcloud - Permissions can be assigned cleanly in later steps **Architectural Context** You now have: ``` Fastmail → user@company.com (email identity) Nextcloud → username (application identity) ``` **Step 1 - Log into Nextcloud as Admin** Open: https://drive.company.com Login: Username: admin Password: (from .env) ⚙️ Step 2 — Navigate to User Management In Nextcloud UI: Top-right avatar → Users ➕ Step 3 — Create Users 3.1 Enter user details For each user: Username: jsmith Display Name: John Smith Email: jsmith@company.com Password: (temporary strong password) 3.2 Assign groups (recommended) Create group: internal Assign all internal users to this group. 3.3 Save user Repeat for all internal users. 🔐 Step 4 — Set Password Policy Recommended ✔ Strong temporary passwords ✔ Users change password on first login 🧪 Step 5 — Validate User Access 5.1 Login test (per user) Open new browser/private window: https://drive.company.com Login as user: ✔ Successful login ✔ Dashboard loads 5.2 Basic file test As user: ✔ Upload file ✔ Create folder ✔ Delete file 🔄 Step 6 — Verify Admin Visibility Return to admin account: ✔ All users listed ✔ Group assignments correct ✔ No duplicate usernames 📧 Step 7 — (Optional) Configure Email Field Ensure each user has email set: jsmith → jsmith@company.com Why this matters ✔ Enables sharing notifications ✔ Required for password reset ✔ Supports future SMTP integration 🧠 Step 8 — Naming Consistency Check Ensure alignment: Email: jsmith@company.com Nextcloud: jsmith Avoid ✖ john.smith in one system and jsmith in another 🔒 Step 9 — Security Considerations Enforce ✔ Unique user accounts (no sharing) ✔ Strong passwords ✔ Minimal admin users Optional (future) ✔ Enable 2FA in Nextcloud ✔ Integrate SSO (future phase) ✅ Validation Checklist [✔] All users created in Nextcloud [✔] Usernames match email identities [✔] Users can log in successfully [✔] File operations work for each user [✔] Groups assigned correctly [✔] Emails populated in profiles