Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Put Caddy, your app, and PostgreSQL in one Docker Compose project, but publish only Caddy’s web ports on the VPS. Caddy forwards requests to the app by its Compose service name; the app connects to PostgreSQL at db:5432 over the Compose network. With no PostgreSQL ports: mapping and no firewall rule exposing port 5432, the database is not directly reachable through a public host port.
How the deployment keeps PostgreSQL private
The public request path is internet → VPS ports 80/443 → Caddy → app. The app’s database traffic follows a separate container-network path: app → db:5432. Compose service names act as hostnames for containers on the same network, so the app does not need a public database address.
- Caddy: publishes TCP ports 80 and 443. UDP 443 may also be published to support HTTP/3.
- App: listens on its container port, such as 3000. It usually does not need a host
ports:mapping; Compose peers can reach it over their shared network. - PostgreSQL: listens on port 5432 inside the Compose network, with no host
ports:mapping.
Docker’s PostgreSQL guide warns that publishing PostgreSQL on all host interfaces as 0.0.0.0:5432 makes it accessible from devices that can reach the host: Docker: Networking and connectivity for PostgreSQL. Leaving out the host mapping avoids that public entry point. It does not prevent the app or other containers on the Compose network from connecting to the database.
Configure DNS and the VPS firewall
For Caddy to obtain a publicly trusted certificate, use a domain whose DNS points to the VPS. Create an A record for the server’s IPv4 address and, if you use IPv6, an AAAA record for its IPv6 address. Make sure the VPS firewall and any provider-level network controls permit inbound TCP 80 and 443 to reach Caddy. Permit UDP 443 as well if you intend to support HTTP/3.
#1 Best Overall
Do not open port 5432 to the public. Check both the VPS firewall and provider networking rules: an absent Docker host-port mapping and a restrictive firewall are separate parts of the boundary. Caddy’s automatic HTTPS provisions and renews certificates when its requirements are met; those include a configured hostname, working public DNS, external reachability on the necessary ports, and persistent writable storage for Caddy’s data: Caddy: Automatic HTTPS.
Use Compose service names for container-to-container traffic
A basic Compose project creates a default network and provides service discovery. Configure Caddy to target the app’s service name and internal listening port—for example, app:3000—and configure the app to connect to db:5432. Do not use localhost to mean another container: inside Caddy, localhost refers to the Caddy container itself.
Rank #2
Caddy’s Docker guidance explains that containers on the same Docker network can reach each other without publishing ports, so the app usually needs no host ports: entry: Caddy: Running with Docker Compose. An expose entry can document an app’s internal port in the Compose file, but it is not what makes the app public or necessary for same-network communication.
Example Compose stack and Caddyfile
This is an illustrative starting point, not a drop-in production configuration. Replace the image placeholders with compatible, pinned versions and adapt the app’s port, database settings, secrets, readiness handling, and storage. Verify the selected PostgreSQL image’s current data-directory layout and initialization behavior before relying on the volume path. Docker’s PostgreSQL guide uses postgres:18 and /var/lib/postgresql; layouts may differ across image releases and setups: Docker: PostgreSQL guide.
Rank #3
- HP MicroServer Gen10 Plus Tower Server for Business with Microsoft Windows Server 2019 OS!
- Intel Xeon E-2224 Quad-Core 3.4GHz 8MB CPU, Up To 4.6GHz Turbo
- 32GB (2 x 16GB) DDR4 PC4-21300 2666MHz Unbuffered Memory
- 16TB (4 x 4TB) 7.2K 6Gb/s SATA 3.5" HDDs in RAID
- Hard drives and memory upgrades included separately NOT installed, installation required.
services:
caddy:
image: caddy:<pinned-version>
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- app
app:
image: <your-app-image>
restart: unless-stopped
environment:
DATABASE_URL: <secret-backed-connection-string-to-db>
expose:
- "3000"
depends_on:
- db
db:
image: postgres:<pinned-version>
restart: unless-stopped
environment:
POSTGRES_PASSWORD: <secret>
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
caddy_data:
caddy_config:
postgres_data:
The app’s connection string should use db as the hostname and 5432 as the port, with credentials supplied through your chosen secret-management method. Use a dedicated, least-privilege database user for the app rather than the PostgreSQL superuser, and keep credentials out of source control. The example’s password placeholder is not a production secret-management solution.
The depends_on entries express startup ordering; they do not establish that PostgreSQL is ready to accept connections before the app tries to connect. Configure the app to retry database connections or implement an appropriate health/readiness design for your stack.
Rank #4
Use a real domain in the Caddyfile and set the upstream to the Compose service name plus the app’s internal listening port:
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
example.com {
reverse_proxy app:3000
}
example.com is a placeholder. Caddy’s reverse proxy directive accepts an upstream such as app:3000: Caddy: reverse_proxy directive. The official Caddy Docker image’s default Caddyfile listens on port 80; specifying a site hostname is necessary for this public HTTPS setup: Official Caddy image.
Best Value
Bring up the stack and verify its boundaries
- Point DNS to the VPS. Configure the domain’s A record and any used AAAA record, then allow inbound web traffic on TCP 80 and 443 through provider networking and the VPS firewall. Add UDP 443 if HTTP/3 is desired.
- Put the services in the same Compose project. The default Compose network is sufficient for a basic setup. If you define custom networks, attach Caddy, the app, and the database to a network that permits the required service-to-service traffic.
- Set the internal destinations. Configure the app for
db:5432and Caddy forapp:<container-port>. These are service names and container ports, not public VPS addresses. - Start the services. From the directory containing the Compose file, run
docker compose up -d. Check the service logs if startup fails, then visit the domain and verify the app responds over HTTPS and Caddy successfully provisions its certificate. - Inspect published ports. Confirm the database has no host binding for port 5432. If you intentionally configured a host-only mapping, make sure it binds to
127.0.0.1, not all interfaces, and separately review firewall policy. - Plan persistence and recovery. Keep Caddy’s data and configuration and PostgreSQL’s data in persistent storage. Document how you back up and restore them, and test restores; Docker volumes alone are not backups.
Choose a separate route for database administration
If you need to administer PostgreSQL from a laptop, do not make direct public exposure the default. Use a deliberately secured access design such as a VPN or an authenticated SSH tunnel. A loopback-only host mapping such as 127.0.0.1:5432:5432 can support host-local tooling; an SSH tunnel can forward a local laptop port through the server to that loopback address. This mapping is not a public database endpoint, but it is also not a universal substitute for reviewing host access and firewall controls.
A mapping written as 5432:5432 typically publishes the port on host interfaces and is not appropriate for this private-database design unless a separate, intentional access boundary controls it. Docker documents both the all-interface exposure risk and loopback binding considerations in its PostgreSQL networking and connectivity guidance.
Persist, update, and back up deliberately
Containers can be replaced; application and database state should not depend on a replaceable container’s writable layer. Persist PostgreSQL data and Caddy’s /data and /config directories. Caddy stores important TLS-related data under /data, so retaining that directory helps preserve operational state across container replacement: Caddy’s Docker Compose guidance.
Choose image versions deliberately rather than using a floating latest tag as a production pin. Review upgrade compatibility, especially PostgreSQL’s image version and storage layout, before updating. Define a backup schedule that fits your recovery needs, store backups separately from the VPS where practical, and verify that restoration works; the presence of a named volume does not itself provide a backup strategy.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

