# Safe deploy on Fly.io

How to deploy critical production services with no downtime and no risk.

## When to use this process

- Critical services that MUST NOT fail (production APIs)
- Significant code changes
- When you're unsure about the stability of the new code

## Safe deploy process

### 1. Verify it compiles locally

```bash
cd [project]/
cargo check
cargo build --release
```

### 2. Create a temporary test app

```bash
cd [project]/fly/

# Copy fly.toml for testing
cp fly.toml fly-test.toml

# Edit fly-test.toml:
# - Change app = "api-name" → app = "api-name-test"
# - Leave everything else as-is
```

### 3. Deploy to the temporary app

```bash
# Deploy to the test app
fly deploy -c fly-test.toml --app api-name-test

# Configure secrets (same as production)
fly secrets set -a api-name-test \
  DATABASE_URL="..." \
  OTHER_SECRET="..."
```

### 4. Verify the temporary app

```bash
# Health check
curl https://api-name-test.fly.dev/health

# Hit critical endpoints
curl https://api-name-test.fly.dev/v1/critical-endpoint

# Check logs
fly logs -a api-name-test
```

### 5. If everything's OK → deploy to production

```bash
# Deploy to production
fly deploy -c fly.toml

# Verify
curl https://api-name.fly.dev/health
```

### 6. Tear down the temporary app

```bash
# Destroy the temporary app
fly apps destroy api-name-test --yes

# Remove the temporary file
rm fly-test.toml
```

## Emergency rollback

If something fails in production:

```bash
# List previous releases
fly releases -a api-name

# Roll back to a previous version
fly deploy -a api-name --image registry.fly.io/api-name:v[PREVIOUS_VERSION]
```

## Pre-deploy checklist

- [ ] `cargo check` passes
- [ ] `cargo build --release` compiles
- [ ] Critical tests pass (if any)
- [ ] Version bumped in `Cargo.toml`
- [ ] DB migrations applied (if any)
- [ ] Secrets configured correctly

## Apps per service

| Service        | Production app      | Test app (temporary)     |
| -------------- | ------------------- | ------------------------ |
| Cryptonite API | rust-api-cryptonite | rust-api-cryptonite-test |
| Pulse          | cryptonite-pulse    | cryptonite-pulse-test    |
| PSMailer API   | psmailer-api        | psmailer-api-test        |
| PSMailer M     | psmailer-m          | psmailer-m-test          |
| Support        | support-api         | support-api-test         |

## Notes

- Temporary apps use the same secrets as production
- The full process adds ~5–10 minutes but avoids downtime
- For trivial changes (typos, log strings), you can deploy directly

---

## Known issues

### Issue #1: don't `cargo update` before a deploy (2026-01-25)

**Problem**: `cargo update` bumps EVERY dependency to its latest version. Some newer dependencies require Rust `edition2024`, which the Dockerfile (Rust 1.84) doesn't support.

**Symptom**:
error: feature `edition2024` is required
The package requires the Cargo feature called `edition2024`

**Fix**:

- NEVER run `cargo update` right before a deploy
- If you already did, revert Cargo.lock: `git checkout HEAD~1 -- Cargo.lock`
- Only add new code; don't touch dependencies

**Rule**: `Cargo.lock` should stay stable between deploys.

---

### Issue #2: new files must be added to the Dockerfile (2026-01-25)

**Problem**: if you add a new file that's used at compile time (like `build.rs`), it must be added to the Dockerfile's COPY.

**Symptom**:
error: environment variable `BUILD_TIME` not defined at compile time

**Fix**: add the file to the COPY in the Dockerfile:
COPY Cargo.toml Cargo.lock build.rs ./

**Rule**: any file that affects compilation must be in the Dockerfile.
