TL;DR: A simple self-hosted web app can be moved onto a VPS, secured with SSH, reverse proxying, systemd, and HTTPS, then extended with endpoint pooling and IP-based request filtering, according to ngrok. The practical lesson is that exposed services fail when identity, binding, and traffic controls are treated as setup details rather than governance decisions.
NHIMG editorial — based on content published by ngrok: a step-by-step guide to self-hosting a web app with a VPS, Caddy, systemd, and ngrok controls
Questions worth separating out
Q: How should security teams reduce privilege when self-hosting web applications?
A: Separate the application, proxy, and administration layers so no single process needs broad authority.
Q: Why do non-root service accounts matter for web app hosting?
A: Non-root service accounts limit what an exploited service can do after compromise.
Q: What do teams get wrong about SSH access to production servers?
A: They often treat SSH as a convenience channel instead of privileged administration.
Practitioner guidance
- Harden SSH as privileged access Require key-based login, change provider defaults immediately, and verify the host fingerprint on first connection to prevent silent host substitution.
- Separate proxy and application privileges Run the application as a non-root process and place port binding, TLS termination, and routing in a distinct proxy process with narrower permissions.
- Use Linux capabilities instead of full root Grant only CAP_NET_BIND_SERVICE or equivalent runtime capability when a service needs low ports, and avoid broad sudo-based startup patterns.
What's in the full article
ngrok's full blog post covers the operational detail this post intentionally leaves for the source:
- Step-by-step VPS setup, including provider selection, SSH login, and moving a compiled binary onto the host.
- Detailed Caddy and systemd configuration, including the exact service layout and low-port binding approach.
- ngrok endpoint pooling setup for multi-host load balancing and failover handling.
- Traffic Policy examples for blocking unwanted requests based on IP intelligence and category data.
👉 Read ngrok's guide to self-hosting a web app on a VPS with Caddy and ngrok →
Self-hosted web apps: what access and runtime controls are missing?
Explore further
Least privilege is the real security control behind simple hosting choices. This guide shows that the main risk in self-hosting is not the app code alone, but how much authority the surrounding processes inherit. SSH defaults, low-port binding, and long-lived service accounts can turn a small deployment into an overly trusted host. For identity teams, the same logic applies to non-human credentials and automation accounts: limit what the runtime can do, not just who can log in.
A question worth separating out:
Q: How can organisations decide whether a reverse proxy is enough for public exposure?
A: A reverse proxy is useful when it is doing more than forwarding traffic. It should terminate TLS, enforce edge policy, and isolate the app from direct public ports. If the proxy is only a pass-through, the organisation has not really changed the exposure model, only the routing path.
👉 Read our full editorial: Self-hosted web apps need tighter access and runtime controls