← All work
DevOps, 2019 – 2026

Hosting and infrastructure

Where the work actually runs: Netcup and DigitalOcean VPSs behind nginx or Caddy, Docker Compose per environment, Vercel for frontends, object storage for media, GPUs on RunPod, and a Mac Mini that serves AI from a desk.

Role
Own, across Ottr products and client work
Context
Ottr Technology and clients
Scale
a dozen production domains, three hosting shapes
Visitorsany domainVercelsites, dashboardsSpaces · S3presigned uploadshost nginx + certbotsingle-app VPScontainer nginxSPA + /apiCaddy edgeon-demand TLS, forward_authNestJS APIstenant headersPostgres · Redislocalhost onlyask endpointis this a tenant?
Three hosting shapes: Vercel for frontends, a VPS with host nginx for single-app boxes, and a Caddy edge with on-demand TLS for multi-tenant products.

Background

Most of what is on this site runs on infrastructure I set up and look after. For an agency, hosting is a cost line, a security surface and the thing clients call about at night, so it has to be cheap, boring and the same shape everywhere.

The problem

Client budgets rule out managed Kubernetes and most managed platforms. Budget VPS providers like Netcup give excellent hardware per rupee but no cloud security groups, no load balancer and no managed TLS. Multi-tenant products add a harder requirement: customers bring their own domains, and each one needs a certificate without anyone touching the server.

Approach

  1. Frontends on Vercel: previews per PR, production on main, no servers to patch.
  2. APIs and databases on a VPS with Docker Compose, one file per environment (dev, staging, prod), images built in CI and pulled from GHCR where the project has a registry, PM2 for the smaller Node and Strapi backends.
  3. Netcup as the default box for price per core: the host firewall (ufw) is the only network gate, so only 80 and 443 are open and every service binds to localhost. Omni-Ledger staging is sized for a Netcup VPS 2000 (8 vCores, 16 GB, 512 GB NVMe).
  4. Two-layer nginx where one app owns the box: host nginx terminates TLS with certbot and proxies to a container nginx that serves the SPA and forwards /api to NestJS (appointment.ottr.com.np).
  5. Caddy with on-demand TLS where tenants bring domains: Caddy asks the API whether a hostname belongs to a tenant before issuing a certificate, rate-limited, then routes through forward_auth that stamps tenant headers onto the request (Omni-Ledger edge, Kiln production, with HSTS and security headers).
  6. Media out of the app server: DigitalOcean Spaces for GC LAL and Baccpacc (with a CDN domain), S3 and CloudFront elsewhere, presigned uploads so the API never proxies bytes.
  7. Special cases: RunPod GPUs started and health-checked by the ACT3 job runner; the Ottr assistant runs on a Mac Mini at the office and reaches the internet through an autossh reverse tunnel to a small VPS.
  8. Operability as a default: Prometheus, Grafana and Loki where the project warrants it, scheduled Postgres backups, and deploy notifications in Discord.

Architecture

VercelMarketing sites, dashboards, storefronts. Preview per PR.
Netcup / DigitalOcean VPSCompose stacks for APIs, Postgres, Redis, workers. ufw, SSH keys only.
Caddy edgeOn-demand TLS for tenant custom domains, forward_auth routing, security headers.
host nginx + certbotSingle-app boxes: TLS at the host, SPA and /api inside the container.
Object storageDO Spaces, S3, CloudFront; presigned direct uploads.
Edge casesRunPod GPUs for ACT3, Mac Mini behind an autossh tunnel for the assistant.

Outcome

  • Production domains include sinceretravels.com, nepalmusicarchive.org, api.gclal.np, api.baccpacc.app, storefront.ottr.com.np and appointment.ottr.com.np.
  • New tenants on Omni-Ledger and Kiln point a domain at the edge and get a certificate on first request, with no deploy.

Lessons

  • On a VPS without security groups, 'bind to localhost' is the most important line in every compose file.
  • On-demand TLS is only safe with an ask endpoint. Without it, anyone can make your server request certificates for any hostname.

Stack

Netcup VPSDigitalOceanAWSVercelDocker ComposeGHCRCaddynginxcertbotufwPM2DO SpacesS3CloudFrontRunPodautosshPrometheusGrafanaLoki