← back to portfolio
case-study

How this site actually runs

This page is hosted by the system it describes. A look under the hood of jimpapadimas.com — the AWS infrastructure, the container setup, the delivery model, and the security decision behind each piece.

AWS EC2Dockernginx GitHub ActionsSSMLet’s Encrypt
overview

The system at a glance

Everything runs on one small EC2 instance in eu-north-1 with a static Elastic IP. A single host nginx terminates TLS and routes each hostname to its own Docker container — one container per site. New projects arrive as subdomains on the same box; no new servers, no new IPs.

There is no public SSH. Operator access and deploys both go through AWS Systems Manager, so port 22 stays closed to the internet entirely. The design goal throughout: spin up a new project freely, while keeping the attack surface and the running cost small.

architecture

Request path

Visitor
https / 443
Bluehost DNS
A record
Elastic IP · EC2
eu-north-1 · :22 closed
host nginx
TLS · vhost router
apex
127.0.0.1:8080
this page
127.0.0.1:8081

DNS points the domain and each subdomain at one Elastic IP — a static address that survives instance restarts. nginx then does the interesting work: it terminates TLS and uses name-based virtual hosting to match the incoming Host header against a per-site server block, forwarding each hostname to the right container.

Every container binds only to 127.0.0.1, so nothing is reachable from the internet except through nginx. Adding a project is three repeatable steps — a DNS record, an nginx server block on a fresh local port, and a Let’s Encrypt certificate — and the architecture above doesn’t change.

delivery

From commit to live

Local dev
git push
GitHub Actions
OIDC · build · Trivy
Amazon ECR
image : sha
SSM
send-command
EC2
pull · up · health-check

A push to main triggers a pipeline that authenticates to AWS via OIDC federation — no cloud keys stored in the repository. It builds the image, scans it with Trivy and fails the build on high-severity findings, then pushes a commit-SHA-tagged image to ECR. Deployment is triggered over SSM: the instance pulls the new image, swaps the container, and runs a health check that rolls back automatically if the new release doesn’t answer.

Status: this is the target delivery model. While the pipeline is being finalised, deploys are done by rebuilding the image on the box — same end state, manual trigger.

security

Deliberate choices

No public SSH

Operator access runs through SSM Session Manager. Port 22 is closed to the internet, and every session is recorded in CloudTrail.

No static cloud keys

CI authenticates with short-lived OIDC tokens; the local CLI uses temporary aws login credentials. Nothing long-lived sits on disk or in the repo.

Least-privilege IAM

The deploy role can push to exactly one ECR repository and send commands to exactly one instance — nothing wider.

Root as break-glass

The root account has MFA and is otherwise untouched; day-to-day work runs as a separate non-root admin identity.

Containers on localhost

Application containers bind to 127.0.0.1 only. The host nginx is the single, deliberate ingress point.

Scanning before ship

Trivy gates the image in CI; ECR also scans on push. A vulnerable image fails the build rather than reaching production.

TLS by default

Let’s Encrypt certificates per hostname, auto-renewing, with HTTP redirected to HTTPS.

Immutable releases

Images are tagged by commit SHA, so any deploy is reproducible and a rollback is a single re-point to the previous tag.

next

Where it’s going

  • subdomainsA repeatable project pattern. Record → nginx block on a new port → certificate. Each new build is a subdomain on the same box.
  • homebrxA stateful app with auth. Its own compose stack — application plus a Postgres database on a private network and a persistent volume, with scheduled backups.
  • wildcard-tlsOne certificate for all subdomains. Moving DNS to Route 53 enables DNS-01 wildcard issuance and drops the per-subdomain certificate step.
  • observabilityEyes on the box. Centralised logs, basic metrics, and alerting so failures surface before a visitor finds them.