|
| 1 | +# CPAB Production VPS Deployment |
| 2 | + |
| 3 | +This deployment runs CPAB behind nginx on `openapi.io.vn`. |
| 4 | + |
| 5 | +- Public API: `https://openapi.io.vn/v1/*` |
| 6 | +- No dashboard/admin/user routes are exposed by this nginx deployment. |
| 7 | +- Public VPS port: `80` |
| 8 | +- Private Docker-only CPAB port: `8318` |
| 9 | +- Cloudflare or the upstream nginx terminates public HTTPS and connects to this origin over HTTP. |
| 10 | + |
| 11 | +## 1. VPS Prerequisites |
| 12 | + |
| 13 | +Install Docker Engine and the Docker Compose plugin on the VPS. |
| 14 | + |
| 15 | +Open inbound TCP port `80` on the VPS firewall/security group. Do not expose port `8318`; it is only used inside the Docker network. |
| 16 | + |
| 17 | +## 2. Copy Files To The VPS |
| 18 | + |
| 19 | +From this repository root on your local machine: |
| 20 | + |
| 21 | +```bash |
| 22 | +SSH_PORT=<your-vps-ssh-port> |
| 23 | +ssh -p "$SSH_PORT" <ssh-user>@<vps-ip> 'sudo mkdir -p /opt/cpab && sudo chown "$USER:$USER" /opt/cpab' |
| 24 | +rsync -az -e "ssh -p $SSH_PORT" devops/ <ssh-user>@<vps-ip>:/opt/cpab/ |
| 25 | +``` |
| 26 | + |
| 27 | +If you prefer `scp`, its SSH port flag is uppercase `-P`: |
| 28 | + |
| 29 | +```bash |
| 30 | +SSH_PORT=<your-vps-ssh-port> |
| 31 | +scp -P "$SSH_PORT" -r devops/* <ssh-user>@<vps-ip>:/opt/cpab/ |
| 32 | +``` |
| 33 | + |
| 34 | +On the VPS: |
| 35 | + |
| 36 | +```bash |
| 37 | +cd /opt/cpab |
| 38 | +``` |
| 39 | + |
| 40 | +The compose file expects this layout: |
| 41 | + |
| 42 | +```text |
| 43 | +/opt/cpab/ |
| 44 | +|-- docker-compose.production.yml |
| 45 | +`-- nginx/ |
| 46 | + `-- openapi.io.vn.conf |
| 47 | +``` |
| 48 | + |
| 49 | +## 3. Create The Runtime Environment |
| 50 | + |
| 51 | +Create `/opt/cpab/.env` on the VPS: |
| 52 | + |
| 53 | +```bash |
| 54 | +POSTGRES_USER=cpab |
| 55 | +POSTGRES_PASSWORD=replace-with-a-strong-database-password |
| 56 | +POSTGRES_DB=cpab |
| 57 | +JWT_SECRET=replace-with-output-of-openssl-rand-hex-32 |
| 58 | +JWT_EXPIRY=720h |
| 59 | +CORS_ORIGINS=https://openapi.io.vn |
| 60 | +HTTP_PORT=80 |
| 61 | +``` |
| 62 | + |
| 63 | +Generate a JWT secret if needed: |
| 64 | + |
| 65 | +```bash |
| 66 | +openssl rand -hex 32 |
| 67 | +``` |
| 68 | + |
| 69 | +## 4. Configure Cloudflare DNS |
| 70 | + |
| 71 | +Create an `A` record: |
| 72 | + |
| 73 | +```text |
| 74 | +Name: openapi |
| 75 | +Target: <vps-public-ip> |
| 76 | +Proxy status: Proxied |
| 77 | +``` |
| 78 | + |
| 79 | +The inner nginx config does not enforce Cloudflare source IP checks. If there is an upstream/front nginx, point it at this service over HTTP and avoid HTTP-to-HTTPS redirects for this origin path. |
| 80 | + |
| 81 | +## 5. Configure Cloudflare TLS |
| 82 | + |
| 83 | +Cloudflare automatically issues and renews the public edge certificate for browser/client traffic to `https://openapi.io.vn`. |
| 84 | + |
| 85 | +This deployment listens on origin port `80` only, so the layer directly in front of this service must connect over HTTP. If Cloudflare connects directly to this service, use Cloudflare SSL/TLS mode `Flexible` for this hostname/zone, or another Cloudflare rule that makes the origin request use HTTP. |
| 86 | + |
| 87 | +Do not use `Full` or `Full (strict)` while this origin only listens on port `80`; those modes make Cloudflare try HTTPS to the origin. |
| 88 | + |
| 89 | +No origin certificate files are needed for this HTTP-only deployment. |
| 90 | + |
| 91 | +## 6. Configure Cloudflare Access |
| 92 | + |
| 93 | +The nginx config only exposes `/v1` and `/v1/*`. Everything else returns `404` before reaching CPAB. |
| 94 | + |
| 95 | +If you still create a Cloudflare Zero Trust application, configure a public bypass for the API path: |
| 96 | + |
| 97 | +```text |
| 98 | +Domain: openapi.io.vn |
| 99 | +Path: /v1* |
| 100 | +Policy: Bypass, Include Everyone |
| 101 | +``` |
| 102 | + |
| 103 | +Do not add a protected `/*` Access application for this hostname unless you also change nginx to expose those dashboard/admin/user paths. With the current nginx config, non-`/v1` paths are intentionally unavailable. |
| 104 | + |
| 105 | +## 7. Start The Stack |
| 106 | + |
| 107 | +On the VPS: |
| 108 | + |
| 109 | +```bash |
| 110 | +cd /opt/cpab |
| 111 | +docker compose -f docker-compose.production.yml pull |
| 112 | +docker compose -f docker-compose.production.yml up -d |
| 113 | +``` |
| 114 | + |
| 115 | +Check container status: |
| 116 | + |
| 117 | +```bash |
| 118 | +docker compose -f docker-compose.production.yml ps |
| 119 | +``` |
| 120 | + |
| 121 | +Check nginx syntax after any config change: |
| 122 | + |
| 123 | +```bash |
| 124 | +docker compose -f docker-compose.production.yml exec nginx nginx -t |
| 125 | +``` |
| 126 | + |
| 127 | +Reload nginx without restarting the whole stack: |
| 128 | + |
| 129 | +```bash |
| 130 | +docker compose -f docker-compose.production.yml exec nginx nginx -s reload |
| 131 | +``` |
| 132 | + |
| 133 | +## 8. Smoke Test |
| 134 | + |
| 135 | +From your local machine: |
| 136 | + |
| 137 | +```bash |
| 138 | +curl -I https://openapi.io.vn/v1/models |
| 139 | +curl -I https://openapi.io.vn/dashboard |
| 140 | +``` |
| 141 | + |
| 142 | +Expected behavior: |
| 143 | + |
| 144 | +- `/v1/*` reaches CPAB publicly, subject to CPAB API key authentication for protected API operations. |
| 145 | +- `/dashboard`, `/user`, `/admin`, `/v0/*`, `/v1beta/*`, and other non-`/v1` paths return `404`. |
| 146 | + |
| 147 | +## 9. Updating |
| 148 | + |
| 149 | +To pull the latest `abwebplode/cpab:development` image and restart: |
| 150 | + |
| 151 | +```bash |
| 152 | +cd /opt/cpab |
| 153 | +docker compose -f docker-compose.production.yml pull cpab |
| 154 | +docker compose -f docker-compose.production.yml up -d cpab |
| 155 | +``` |
| 156 | + |
| 157 | +## Cloudflare SSL Automation Answer |
| 158 | + |
| 159 | +Yes, Cloudflare automatically issues the public edge SSL certificate for `openapi.io.vn` when the DNS record is active on Cloudflare. That covers client-to-Cloudflare HTTPS. |
| 160 | + |
| 161 | +This HTTP-only origin deployment does not need Cloudflare to install any certificate inside nginx, because nginx no longer listens on `443`. Cloudflare serves HTTPS publicly and connects to the VPS over HTTP port `80`. |
| 162 | + |
| 163 | +If you later want encrypted Cloudflare-to-origin traffic again, re-enable nginx `443`, mount an origin certificate, and switch Cloudflare back to `Full (strict)`. |
| 164 | + |
| 165 | +## References |
| 166 | + |
| 167 | +- Cloudflare Universal SSL: https://developers.cloudflare.com/ssl/edge-certificates/universal-ssl/ |
| 168 | +- Cloudflare Flexible mode: https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/flexible/ |
| 169 | +- Cloudflare Access application paths: https://developers.cloudflare.com/cloudflare-one/access-controls/policies/app-paths/ |
| 170 | +- Cloudflare Access policies: https://developers.cloudflare.com/cloudflare-one/access-controls/policies/ |
0 commit comments