24 KiB
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
ssh-keygen -t ed25519 -C "admin@company.com"
Accept default path:
C:\Users\<user>\.ssh\id_ed25519
Copy public key
</> 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
- Login to DigitalOcean
- Click Create
- 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
ssh root@<IP>
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
adduser adminuser
usermod -aG sudo adminuser
Copy SSH key
</> Bash
rsync --archive --chown=adminuser:adminuser ~/.ssh /home/adminuser
Disable root login
</> Bash
sudo tee /etc/ssh/sshd_config > /dev/null << 'EOF'
PermitRootLogin no
PasswordAuthentication no
EOF
</> Bash
sudo systemctl restart ssh
Test login
</> Powershell
ssh adminuser@<IP>
Update system
</> 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
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
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: <DROPLET_IP>
TTL: 300
Validate
</> 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
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
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
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
Enable Docker
</> Bash
sudo systemctl enable docker
sudo systemctl start docker
sudo usermod -aG docker adminuser
Reconnect SSH.
Test
</> 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
mkdir -p ~/apps/nextcloud
cd ~/apps/nextcloud
Create .env
</> 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=<IP>
EOF
Create docker-compose
</> 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
docker compose up -d
Validate
http://<IP>: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
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx
Test:
http://<IP> → 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
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
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:
is:
- Trusted (no browser warnings)
- Encrypted (HTTPS)
- Routed through Nginx to Nextcloud
</> 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
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
sudo nginx -t
sudo systemctl reload nginx
Fix Nextcloud
</> 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
- ✔ Login works
- ✔ Upload works
- ✔ Download works
- ✔ No warnings
- ✔ HTTPS padlock present
Cleanup
</> 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)
For this pilot:
✔ Keep usernames identical (jsmith ↔ jsmith@company.com) Prerequisites [✔] Nextcloud deployed and accessible (Task 3.15) [✔] HTTPS working: https://drive.company.com [✔] Admin account available [✔] Email accounts created (Task 3.18) 🌐 Step 1 — Log into Nextcloud as Admin
Open:
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:
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