Skip to content

Commit bd6194b

Browse files
committed
Updated deployment docs
1 parent f1866d9 commit bd6194b

2 files changed

Lines changed: 167 additions & 145 deletions

File tree

DIGITALOCEAN.md

Lines changed: 167 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,141 @@
1-
# DigitalOcean Configuration
1+
# Freehub Deployment on Digital Ocean
22

3-
Configuration changes required for running Freehub (Rails 2.3) on DigitalOcean.
3+
https://freehub.bikekitchen.org is hosted on the [DigitalOcean App Platform](https://www.digitalocean.com/products/app-platform) with a managed MySQL database.
44

5-
---
6-
7-
### 1. App Platform Environment Variables
8-
9-
Set these app-level environment variables:
5+
> **Tip**: [Claude Code](https://docs.anthropic.com/en/docs/claude-code) is a helpful assistant for configuring and managing the DigitalOcean application environment.
106
11-
1. Go to **App Platform** → your app → **Settings**
12-
2. Under **App-Level Environment Variables**, add:
13-
- `RAILS_ENV` = `production`
14-
- `SITE_URL` = `https://freehub.bikekitchen.org` (staging: `https://freehub-staging.bikekitchen.org`)
15-
- `AIRBRAKE_API_KEY` = *(Airbrake API key)*
16-
- `RESEND_API_KEY` = *(Resend SMTP API key)*
7+
## Overview
178

18-
`RAILS_ENV` is required — without it, Rails defaults to development mode and won't use the production database configuration.
9+
| Component | Service | Notes |
10+
|-----------|---------|-------|
11+
| **Application** | DigitalOcean App Platform | Rails 2.3 app in Docker |
12+
| **Database** | DigitalOcean Managed MySQL 8 | Premium AMD, 2GB RAM |
13+
| **DNS** | Bluehost | bikekitchen.org domain |
14+
| **SSL** | DigitalOcean (Let's Encrypt) | Auto-managed, auto-renewed |
15+
| **Email** | Resend | Transactional emails via SMTP |
16+
| **Error Tracking** | Airbrake | Automatic error notifications |
17+
| **Analytics** | Google Analytics (GA4) | Usage statistics |
18+
| **Uptime Monitoring** | DigitalOcean | Alerts on downtime |
1919

2020
---
2121

22-
### 2. MySQL Password Encryption (Legacy)
23-
24-
Changed the `freehub_app` database user to use legacy MySQL 5.x password encryption:
25-
26-
1. Go to **Databases** → your database cluster → **Users & Databases**
27-
2. Find the `freehub_app` user
28-
3. Click **More** (three dots) → **Edit Password Encryption**
29-
4. Select **Legacy - MySQL 5.x** (`mysql_native_password`) → **Save**
22+
## DNS
3023

31-
**Why**: The old `mysql` gem in Rails 2.3 doesn't support MySQL 8.0's default `caching_sha2_password` authentication plugin.
24+
DNS for `freehub.bikekitchen.org` is managed in **Bluehost** (bikekitchen.org domain).
3225

33-
**Security note**: `mysql_native_password` is less secure than `caching_sha2_password`, but is required for compatibility with older MySQL client libraries.
26+
```
27+
freehub.bikekitchen.org CNAME freehub-app-s65go.ondigitalocean.app
28+
freehub-staging.bikekitchen.org CNAME freehub-staging-hkbze.ondigitalocean.app
29+
```
3430

3531
---
3632

37-
### 3. Disable sql_require_primary_key
33+
## External Services
3834

39-
We disabled `sql_require_primary_key` on the DigitalOcean managed MySQL database to support Rails 2.3 HABTM (has_and_belongs_to_many) join tables which don't have primary keys.
35+
### Email (Resend)
4036

41-
**What was changed:**
37+
Transactional emails (account activation, password reset, etc.) are sent via [Resend](https://resend.com) SMTP.
4238

43-
Disabled `sql_require_primary_key` via DigitalOcean API:
39+
- **SMTP host**: `smtp.resend.com`
40+
- **Port**: 2587
41+
- **Domain**: `bikekitchen.org`
4442

45-
```bash
46-
doctl databases configuration update af845ccd-eb8d-4f0d-99b5-2c8cffc71681 --engine mysql --config-json '{"sql_require_primary_key": false}'
47-
```
43+
The API key is configured via the `RESEND_API_KEY` environment variable.
4844

49-
**Why**: The `roles_users` join table (for the `Role has_and_belongs_to_many :users` relationship) doesn't have a primary key. Rails 2.3 HABTM doesn't support primary keys on join tables, but DigitalOcean's default MySQL 8.0 configuration requires them for replication purposes.
45+
### Error Tracking (Airbrake)
5046

51-
**Affected tables:**
52-
53-
| Table | Purpose | Notes |
54-
|-------|---------|-------|
55-
| `roles_users` | User role assignments (HABTM join table) | Small, rarely updated |
56-
| `schema_info` | Legacy Rails 1.x migration tracking | Not used at runtime, may exist in old backups |
57-
| `schema_migrations` | Rails migration version tracking | Only written during deploys |
47+
Errors are tracked by [Airbrake](https://airbrake.io) and notifications are sent to configured team members.
5848

59-
**Implications:**
49+
The API key is configured via the `AIRBRAKE_API_KEY` environment variable.
6050

61-
- **Single-node cluster**: No impact
62-
- **Multi-node cluster**: Potential replication lag on updates/deletes to tables without PKs, since MySQL must do full table scans to identify rows. Risk is minimal for `roles_users` due to its small size and infrequent updates.
51+
### Analytics (Google Analytics)
6352

64-
**How to revert:**
53+
Usage statistics are tracked in Google Analytics (GA4). Access via the Google Analytics dashboard.
6554

66-
1. Add a primary key to `roles_users`:
67-
```sql
68-
ALTER TABLE roles_users ADD COLUMN id INT NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
69-
```
55+
---
7056

71-
2. Re-enable the requirement:
72-
```bash
73-
doctl databases configuration update af845ccd-eb8d-4f0d-99b5-2c8cffc71681 --engine mysql --config-json '{"sql_require_primary_key": true}'
74-
```
57+
## DigitalOcean Configuration
7558

76-
3. Optionally, convert the HABTM to `has_many :through` in the Rails models for a cleaner long-term solution.
59+
### Apps
7760

78-
---
61+
| App | URL | Auto-deploy |
62+
|-----|-----|-------------|
63+
| **Production** | https://freehub.bikekitchen.org | OFF (manual) |
64+
| **Staging** | https://freehub-staging.bikekitchen.org | ON (from master) |
7965

80-
### 4. Database Tier
66+
**Note**: Staging is archived when not in use to save costs. Archiving is free for up to 20 apps and 3 months; DigitalOcean may charge beyond that. See [Setting Up Staging](#setting-up-staging) if you need to recreate it.
8167

82-
Using **Premium AMD, 1 vCPU, 2GB RAM** ($36/mo) with NVMe storage. Upgraded from Regular 1 vCPU/1GB for better query performance — dedicated CPU and NVMe disk significantly improve aggregation queries across large tables.
68+
### Environment Variables
8369

84-
---
70+
Set in App Platform → Settings → App-Level Environment Variables:
8571

86-
### 5. Database Trusted Sources
72+
| Variable | Value |
73+
|----------|-------|
74+
| `RAILS_ENV` | `production` |
75+
| `SITE_URL` | `https://freehub.bikekitchen.org` |
76+
| `AIRBRAKE_API_KEY` | *(Airbrake API key)* |
77+
| `RESEND_API_KEY` | *(Resend SMTP API key)* |
8778

88-
By default, DigitalOcean managed databases block external connections. To allow developers to connect directly to the database:
79+
`RAILS_ENV` is required — without it, Rails defaults to development mode.
8980

90-
1. Go to **Databases** → your database cluster → **Settings**
91-
2. Scroll to **Trusted Sources**
92-
3. Add the developer's IP address (find it with `curl ifconfig.me`)
81+
### Database
9382

94-
**Note**: For security, avoid using "Allow all IPv4". Add specific IPs as needed.
83+
**Tier**: Premium AMD, 1 vCPU, 2GB RAM ($36/mo) with NVMe storage.
9584

96-
**To connect locally**:
85+
**Connect locally** (requires your IP in Trusted Sources):
9786
```bash
98-
mysql -h db-mysql-sfo3-freehub-do-user-32369540-0.d.db.ondigitalocean.com -P 25060 -u doadmin -p --ssl-mode=REQUIRED freehub_production
87+
mysql -h db-mysql-sfo3-freehub-do-user-32369540-0.d.db.ondigitalocean.com \
88+
-P 25060 -u doadmin -p --ssl-mode=REQUIRED freehub_production
9989
```
10090

101-
---
91+
To add your IP to Trusted Sources:
92+
1. Go to **Databases** → your cluster → **Settings****Trusted Sources**
93+
2. Add your IP (find it with `curl ifconfig.me`)
10294

103-
## Deployment Workflow
95+
### SSL Certificates
10496

105-
### Overview
97+
Managed automatically by App Platform via Let's Encrypt. Certificates auto-renew before expiration.
10698

107-
- **Staging** (`freehub-staging`): Auto-deploys from `master` branch
108-
- **Production** (`freehub-production`): Manual deploy from `master` branch
99+
### Uptime Monitoring
109100

110-
### App Platform Configuration
101+
Checks that the site is accessible and emails `freehub@bikekitchen.org` if down for 2+ minutes.
111102

112-
Configure each app's deploy settings:
103+
| Resource | ID |
104+
|----------|-----|
105+
| Uptime check | `290bf29b-2886-4c2f-bedb-eef81a150606` |
106+
| Down alert | `f77f8306-b692-4a21-96e9-898618c437a2` |
113107

114-
1. **Staging**: Settings → Component → Auto-deploy: **ON**
115-
2. **Production**: Settings → Component → Auto-deploy: **OFF**
108+
```bash
109+
# View status
110+
doctl monitoring uptime get 290bf29b-2886-4c2f-bedb-eef81a150606
116111

117-
### Making Changes
112+
# List alerts
113+
doctl monitoring uptime alert list 290bf29b-2886-4c2f-bedb-eef81a150606
114+
```
118115

119-
1. **Create a feature branch** (optional for small changes):
120-
```bash
121-
git checkout -b my-feature
122-
# make changes
123-
git commit -m "Description of changes"
124-
git push origin my-feature
125-
```
116+
---
126117

127-
2. **Merge to master**:
118+
## Deployment
119+
120+
### Making Changes
121+
122+
1. **Push to master** — automatically deploys to staging
128123
```bash
129124
git checkout master
130-
git merge my-feature
125+
git commit -m "Description of changes"
131126
git push origin master
132127
```
133-
This automatically deploys to **staging**.
134128

135-
3. **Test on staging**:
136-
- Visit the staging app URL
137-
- Verify changes work correctly
129+
2. **Test on staging** — verify changes work at staging URL
138130

139-
4. **Deploy to production**:
140-
- Go to **App Platform**`freehub-production`**Actions****Deploy**
141-
- Or use CLI: `doctl apps create-deployment e1229281-8c8b-46ec-a930-9737caab742d`
131+
3. **Deploy to production**:
132+
- App Platform → `freehub-production`**Actions****Deploy**
133+
- Or: `doctl apps create-deployment e1229281-8c8b-46ec-a930-9737caab742d`
142134

143-
5. **Tag the release** (for history):
135+
4. **Tag the release**:
144136
```bash
145-
git tag v1.0.1
146-
git push origin v1.0.1
137+
git tag v2.0.1
138+
git push origin v2.0.1
147139
```
148140

149141
### Tagging Conventions
@@ -153,11 +145,89 @@ Use semantic versioning: `vMAJOR.MINOR.PATCH`
153145
- `v1.1.0` - minor: new features, backwards compatible
154146
- `v2.0.0` - major: breaking changes
155147

156-
List existing tags:
148+
### Rollback
149+
150+
In App Platform console: **Activity** → select previous deployment → **Redeploy**.
151+
152+
---
153+
154+
## Setting Up Staging
155+
156+
If the staging app was deleted (or auto-deleted after 3 months of being archived), follow these steps to recreate it.
157+
158+
### 1. Create the App
159+
160+
1. Go to **App Platform****Create App**
161+
2. Connect to the **GitHub** repository (`asalant/freehub`)
162+
3. Select the `master` branch
163+
4. Enable **Autodeploy**
164+
165+
### 2. Configure the App
166+
167+
1. Set **Instance Size** to a single basic instance
168+
2. Select the same **VPC** as production (SFO3)
169+
3. Add the existing database:
170+
- Click **Add Resource****Database**
171+
- Select **Previously Created DigitalOcean Database**
172+
- Choose the existing cluster and select the `freehub_staging` database
173+
174+
### 3. Set Environment Variables
175+
176+
Add these app-level environment variables:
177+
178+
| Variable | Value |
179+
|----------|-------|
180+
| `RAILS_ENV` | `production` |
181+
| `SITE_URL` | `https://freehub-staging.bikekitchen.org` |
182+
| `AIRBRAKE_API_KEY` | *(Airbrake API key)* |
183+
| `RESEND_API_KEY` | *(Resend SMTP API key)* |
184+
185+
### 4. Update DNS
186+
187+
After the app is created, update the CNAME in Bluehost to point to the new app URL:
188+
189+
```
190+
freehub-staging.bikekitchen.org CNAME <new-app-name>.ondigitalocean.app
191+
```
192+
193+
### 5. Add Custom Domain
194+
195+
1. Go to **Settings****Domains**
196+
2. Add `freehub-staging.bikekitchen.org`
197+
3. SSL certificate will be provisioned automatically
198+
199+
### 6. Archive When Done Testing
200+
201+
When not actively using staging, archive it to save costs:
202+
203+
**App Platform**`freehub-staging`**Settings****Archive App**
204+
205+
Archiving is free for up to 3 months (and up to 20 apps). DigitalOcean may charge for longer archives.
206+
207+
---
208+
209+
## Database Compatibility Notes
210+
211+
These settings were required for Rails 2.3 compatibility with MySQL 8.
212+
213+
### Legacy Password Encryption
214+
215+
The `freehub_app` user uses legacy MySQL 5.x password encryption (`mysql_native_password`) because the old `mysql` gem doesn't support MySQL 8.0's default `caching_sha2_password`.
216+
217+
To configure: **Databases** → cluster → **Users & Databases**`freehub_app`**Edit Password Encryption****Legacy - MySQL 5.x**
218+
219+
### Disabled sql_require_primary_key
220+
221+
Rails 2.3 HABTM join tables don't have primary keys, but MySQL 8 requires them by default.
222+
157223
```bash
158-
git tag -l
224+
doctl databases configuration update af845ccd-eb8d-4f0d-99b5-2c8cffc71681 \
225+
--engine mysql --config-json '{"sql_require_primary_key": false}'
159226
```
160227

161-
### Rollback
228+
**Affected tables:**
162229

163-
To rollback production to a previous version, redeploy a previous commit from the App Platform console under **Activity** → select a previous deployment → **Redeploy**.
230+
| Table | Purpose |
231+
|-------|---------|
232+
| `roles_users` | User role assignments (HABTM join table) |
233+
| `schema_migrations` | Rails migration tracking |

MIGRATION_CHECKLIST.md

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)