|
| 1 | +--- |
| 2 | +title: DigitalOcean App Platform |
| 3 | +--- |
| 4 | + |
| 5 | +# Deploy nxt-sts on DigitalOcean App Platform |
| 6 | + |
| 7 | +This guide deploys `nxt-sts` on [DigitalOcean App Platform](https://docs.digitalocean.com/products/app-platform/) as a web service. Choose one of two source options: |
| 8 | + |
| 9 | +1. **Pre-built GHCR image** — pull a released container from GitHub Container Registry. |
| 10 | +2. **GitHub repository build** — App Platform builds the image from the repository `Dockerfile` on each deploy. |
| 11 | + |
| 12 | +Both paths produce the same runtime: a stateless Spring Boot service listening on port **8080** with health checks at `/actuator/health`. |
| 13 | + |
| 14 | +## Prerequisites |
| 15 | + |
| 16 | +- DigitalOcean account with App Platform access. |
| 17 | +- For GHCR deploy: a published image at `ghcr.io/nxtgrid/nxt-sts` (created when a `v*.*.*` tag is pushed to the repository). |
| 18 | +- For GitHub build: DigitalOcean authorized to access the `nxtgrid/nxt-sts` repository. |
| 19 | +- If the GHCR package is private: a GitHub personal access token with `read:packages` scope, added as a container registry credential in App Platform. |
| 20 | + |
| 21 | +## Shared runtime settings |
| 22 | + |
| 23 | +Configure the HTTP port during app creation. Health checks are configured separately after the app exists — see [Configure health checks](#configure-health-checks-settings-tab) below. |
| 24 | + |
| 25 | +| Setting | Value | |
| 26 | +|---|---| |
| 27 | +| Component type | Web Service | |
| 28 | +| HTTP port | `8080` | |
| 29 | + |
| 30 | +Optional environment variables (safe defaults are committed in the repository): |
| 31 | + |
| 32 | +| Variable | Purpose | Default | |
| 33 | +|---|---|---| |
| 34 | +| `SERVER_PORT` | HTTP listen port | `8080` | |
| 35 | +| `SPRING_APPLICATION_NAME` | Service name in logs/actuator | `nxt-sts` | |
| 36 | + |
| 37 | +No database, Redis, or other backing services are required — `nxt-sts` is stateless. |
| 38 | + |
| 39 | +## Option A — Deploy from GHCR |
| 40 | + |
| 41 | +Use this when you want App Platform to run a pre-built release image without rebuilding from source. |
| 42 | + |
| 43 | +### 1. Create the app |
| 44 | + |
| 45 | +1. In the DigitalOcean control panel, go to **Apps → Create App**. |
| 46 | +2. Choose **Container Registry** (or **Deploy from a container image** depending on UI version). |
| 47 | +3. Select **GitHub Container Registry (GHCR)** as the registry type. |
| 48 | +4. Set the image to `ghcr.io/nxtgrid/nxt-sts` with tag `latest` or a specific version (e.g. `v1.0.0`). |
| 49 | + |
| 50 | +Pinning a version tag is recommended for production; `latest` tracks the most recent release. |
| 51 | + |
| 52 | +### 2. Configure registry access (private packages only) |
| 53 | + |
| 54 | +If the GHCR package is not public: |
| 55 | + |
| 56 | +1. Create a GitHub PAT with `read:packages` scope. |
| 57 | +2. In App Platform, add a registry credential for GHCR (username: your GitHub username, password: the PAT). |
| 58 | +3. Attach the credential to the app component. |
| 59 | + |
| 60 | +### 3. Set HTTP port |
| 61 | + |
| 62 | +1. Set **HTTP port** to `8080` (matches `EXPOSE 8080` in the Dockerfile). |
| 63 | +2. Leave the component as a **Web Service** (not a worker or job). |
| 64 | + |
| 65 | +Health checks are configured after app creation in **Settings** — the default TCP check on port `8080` is usually sufficient for first deploy (see [Configure health checks](#configure-health-checks-settings-tab)). |
| 66 | + |
| 67 | +### 4. Deploy |
| 68 | + |
| 69 | +Review the plan and create the app. App Platform pulls the image and starts the container. |
| 70 | + |
| 71 | +On first deploy, App Platform uses the default TCP readiness check on port `8080`. No change is required if the deploy succeeds. |
| 72 | + |
| 73 | +To roll forward, push a new version tag in the repository (which triggers the GHCR release workflow), then update the App Platform component tag and redeploy. |
| 74 | + |
| 75 | +## Option B — Build from GitHub |
| 76 | + |
| 77 | +Use this when you want App Platform to build from the repository `Dockerfile` — for example during development, on a feature branch, or when you prefer not to depend on GHCR. |
| 78 | + |
| 79 | +### 1. Create the app |
| 80 | + |
| 81 | +1. In the DigitalOcean control panel, go to **Apps → Create App**. |
| 82 | +2. Choose **GitHub** as the source and authorize repository access if prompted. |
| 83 | +3. Select repository **`nxtgrid/nxt-sts`**. |
| 84 | +4. Select branch **`main`**. |
| 85 | +5. App Platform detects the root **`Dockerfile`** and configures a Docker build automatically. |
| 86 | + |
| 87 | +### 2. Set HTTP port |
| 88 | + |
| 89 | +1. Set **HTTP port** to `8080`. |
| 90 | +2. Confirm App Platform detected the root **`Dockerfile`** for the Docker build. |
| 91 | + |
| 92 | +Configure the HTTP readiness probe after the first deploy only if you want stricter checks — see [Configure health checks](#configure-health-checks-settings-tab). |
| 93 | + |
| 94 | +### 3. Enable auto-deploy (optional) |
| 95 | + |
| 96 | +Turn on **Autodeploy** if App Platform should rebuild and redeploy on every push to the selected branch. |
| 97 | + |
| 98 | +### 4. Deploy |
| 99 | + |
| 100 | +Create the app. App Platform runs the multi-stage Docker build (Maven compile inside the build stage, JRE-only runtime image) and starts the service. |
| 101 | + |
| 102 | +## Configure health checks (Settings tab) |
| 103 | + |
| 104 | +Health checks are not configurable during initial app creation. After the first deploy, they appear under **Settings → Components → your web service → Health Checks**. |
| 105 | + |
| 106 | +### Default: TCP readiness check |
| 107 | + |
| 108 | +App Platform enables a **readiness** check by default: **TCP** on port `8080`, with no HTTP path or extra timing configured. This is the check that runs during deploy — it passes when something is listening on the port. |
| 109 | + |
| 110 | +For `nxt-sts`, that default is often enough. Spring Boot binds to port `8080` when the HTTP server starts, so a TCP probe on `8080` typically lets the first deploy succeed without any changes. |
| 111 | + |
| 112 | +**You do not need to change the default** if deploys complete and `/actuator/health` returns `UP` afterward. |
| 113 | + |
| 114 | +What you may see labelled "liveness" in the UI is sometimes still this default readiness probe (TCP, minimal settings) — not a separate restart-on-failure liveness probe. A true liveness check is only present if you explicitly added one via **Add liveness check**. |
| 115 | + |
| 116 | +### Optional upgrade: HTTP readiness check |
| 117 | + |
| 118 | +Switching the readiness check from **TCP** to **HTTP** is recommended for production, but not required: |
| 119 | + |
| 120 | +- **TCP** — port is open (may pass before Spring context is fully ready). |
| 121 | +- **HTTP** on `/actuator/health` — confirms the actuator reports `{"status":"UP"}`. |
| 122 | + |
| 123 | +To upgrade: |
| 124 | + |
| 125 | +1. Open **Apps** → your `nxt-sts` app → **Settings** tab. |
| 126 | +2. Under **Components**, click the web service component. |
| 127 | +3. Scroll to **Health Checks** and click **Edit**. |
| 128 | +4. Set **Type** to **HTTP**, **Port** to `8080`, **HTTP Path** to `/actuator/health`. |
| 129 | +5. Set timing to match the repository `Dockerfile` `HEALTHCHECK`: |
| 130 | + |
| 131 | +```dockerfile |
| 132 | +HEALTHCHECK --interval=30s --timeout=5s --start-period=15s \ |
| 133 | + CMD wget -qO- http://localhost:8080/actuator/health || exit 1 |
| 134 | +``` |
| 135 | + |
| 136 | +| App Platform field | Value | Dockerfile flag | |
| 137 | +|---|---|---| |
| 138 | +| Initial delay | `15` seconds | `--start-period=15s` | |
| 139 | +| Period | `30` seconds | `--interval=30s` | |
| 140 | +| Timeout | `5` seconds | `--timeout=5s` | |
| 141 | +| Success threshold | `1` (default) | — (App Platform only; one passing probe marks the deploy healthy) | |
| 142 | +| Failure threshold | `9` (default) | — (App Platform only; no Docker equivalent) | |
| 143 | + |
| 144 | +**Success / failure thresholds:** keep DigitalOcean defaults **`1`** and **`9`**. With a `30` s period, nine consecutive failures means roughly 4.5 minutes of sustained probe failure before App Platform rolls back — a good match for JVM cold starts when combined with the `15` s initial delay. Lower the failure threshold only if you want faster rollback on a genuinely broken deploy. |
| 145 | + |
| 146 | +6. Click **Save** (triggers a redeploy with the new probe settings). |
| 147 | + |
| 148 | +### Liveness check (optional) |
| 149 | + |
| 150 | +A **liveness** probe is separate from readiness. It restarts the container if probes keep failing while the app is already running. You only need this if you explicitly click **Add liveness check** — the default TCP entry is not that. |
| 151 | + |
| 152 | +If you add one, use **HTTP** on port `8080` with path `/actuator/health`, and the same timing as the readiness check (`15` s initial delay, `30` s period, `5` s timeout). |
| 153 | + |
| 154 | +If you prefer infrastructure-as-code over the UI, the equivalent app-spec block is in [App spec reference](#app-spec-reference-optional) below. |
| 155 | + |
| 156 | +## Post-deploy verification |
| 157 | + |
| 158 | +Replace `<app-url>` with the App Platform default hostname or your custom domain. |
| 159 | + |
| 160 | +**Health check:** |
| 161 | + |
| 162 | +```bash |
| 163 | +curl -s https://<app-url>/actuator/health |
| 164 | +``` |
| 165 | + |
| 166 | +Expected response: |
| 167 | + |
| 168 | +```json |
| 169 | +{"status":"UP"} |
| 170 | +``` |
| 171 | + |
| 172 | +**Service index:** |
| 173 | + |
| 174 | +```bash |
| 175 | +curl -s https://<app-url>/ |
| 176 | +``` |
| 177 | + |
| 178 | +Returns JSON listing available endpoints including `/token`, `/swagger`, and `/actuator/health`. |
| 179 | + |
| 180 | +**Token generation smoke test:** |
| 181 | + |
| 182 | +```bash |
| 183 | +curl -s -X POST https://<app-url>/token \ |
| 184 | + -H "Content-Type: application/json" \ |
| 185 | + -d '{ |
| 186 | + "type": "TOP_UP", |
| 187 | + "issueDate": "2024-03-15T10:30:00", |
| 188 | + "randomNumber": 3, |
| 189 | + "decoderKey": "0123456789ABCDEF", |
| 190 | + "kwh": 0.5 |
| 191 | + }' |
| 192 | +``` |
| 193 | + |
| 194 | +Use a real decoder key in non-test environments. Always call `/token` over HTTPS in production. |
| 195 | + |
| 196 | +## App spec reference (optional) |
| 197 | + |
| 198 | +Both options can also be defined in an App Platform spec file. Adjust `branch`, `tag`, and registry credentials to match your setup. |
| 199 | + |
| 200 | +**GHCR image:** |
| 201 | + |
| 202 | +```yaml |
| 203 | +name: nxt-sts |
| 204 | +services: |
| 205 | + - name: api |
| 206 | + image: |
| 207 | + registry_type: GHCR |
| 208 | + registry: ghcr.io |
| 209 | + repository: nxtgrid/nxt-sts |
| 210 | + tag: v1.0.0 |
| 211 | + http_port: 8080 |
| 212 | + instance_count: 1 |
| 213 | + instance_size_slug: basic-xxs |
| 214 | + health_check: |
| 215 | + http_path: /actuator/health |
| 216 | + port: 8080 |
| 217 | + initial_delay_seconds: 15 |
| 218 | + period_seconds: 30 |
| 219 | + timeout_seconds: 5 |
| 220 | + success_threshold: 1 |
| 221 | + failure_threshold: 9 |
| 222 | +``` |
| 223 | +
|
| 224 | +**GitHub + Dockerfile:** |
| 225 | +
|
| 226 | +```yaml |
| 227 | +name: nxt-sts |
| 228 | +services: |
| 229 | + - name: api |
| 230 | + github: |
| 231 | + repo: nxtgrid/nxt-sts |
| 232 | + branch: main |
| 233 | + deploy_on_push: true |
| 234 | + dockerfile_path: Dockerfile |
| 235 | + http_port: 8080 |
| 236 | + instance_count: 1 |
| 237 | + instance_size_slug: basic-xxs |
| 238 | + health_check: |
| 239 | + http_path: /actuator/health |
| 240 | + port: 8080 |
| 241 | + initial_delay_seconds: 15 |
| 242 | + period_seconds: 30 |
| 243 | + timeout_seconds: 5 |
| 244 | + success_threshold: 1 |
| 245 | + failure_threshold: 9 |
| 246 | +``` |
| 247 | +
|
| 248 | +## Failure modes |
| 249 | +
|
| 250 | +| Symptom | Likely cause | What to check | |
| 251 | +|---|---|---| |
| 252 | +| Deploy fails pulling image | GHCR auth or missing release tag | Registry credentials; confirm a `v*.*.*` tag exists and the release workflow completed | |
| 253 | +| Health check never passes | TCP-only check or wrong path/port | In **Settings → Health Checks**, switch type to **HTTP**, port `8080`, path `/actuator/health`; set initial delay to `15` s if Spring Boot is still starting | |
| 254 | +| Build fails from GitHub | Docker build error in App Platform | Build logs; confirm `Dockerfile` and `./mvnw` are present on `main` | |
| 255 | +| 502/503 after deploy | Container crash on startup | App Platform runtime logs; verify Java 17 JRE image built successfully | |
| 256 | +| 400 on `/token` | Validation rejection | Swagger UI at `/swagger` for schema; check `randomNumber` (0–15) and 16-char hex `decoderKey` | |
0 commit comments