Published: 17 October 2026
A content delivery network is not magic. It is a set of cache servers placed near your users, plus a way to send each user to the nearest one. Commercial CDNs bill for every gigabyte that leaves those caches. If you push a lot of cacheable traffic to a known region, you can build the same thing from a few unmetered dedicated servers and pay a fixed amount every month, however much you serve. This guide covers when that makes sense, where to put the servers, what it costs, and the nginx configuration that does the caching.
When your own CDN makes sense
It pays off when three things are true:
- The traffic is heavy. Tens or hundreds of terabytes a month of video segments, downloads, game patches, images or software releases.
- The traffic is cacheable. The same files are requested again and again, so each edge fetches a file from your origin once and then serves it many times.
- The audience is regional. Your users are mostly in Europe, or mostly in a few countries, so three to five locations cover them well.
It does not pay off when your users are spread over dozens of countries on several continents, because a commercial CDN has hundreds of locations and you will not match that. It also makes little sense for a site that serves a few terabytes a month, where a CDN's free or entry tier costs less than one server. And if you depend on a CDN's web application firewall, bot filtering or edge functions, you would have to rebuild those yourself.
The four parts
- Origin. The server that holds the real files. It only talks to the edges, never to the public.
- Edges. Cache servers in each region. They answer users, keep popular files on disk and in RAM, and fetch anything missing from the origin.
- Routing. GeoDNS, which answers each DNS query with the address of the edge closest to the user, and removes an edge that stops responding.
- Certificates. Every edge serves your domain over HTTPS, so each one needs a valid certificate.
Where to put the edges
Our measured latency table makes this a simple exercise. A three edge layout for Europe could be Frankfurt, Vienna and Bucharest. With those three, every one of our European locations is within 11.9 ms of an edge: Amsterdam is 6.2 ms from Frankfurt, Warsaw 11.9 ms from Vienna and Sofia 7.5 ms from Bucharest.
Dedicated servers are available in ten European countries and the United States, so you can follow your audience rather than a fixed map. If France or Spain matters, add an edge in Paris or Madrid. For Italy and Switzerland, Milan. Every country has the same price, so the layout is decided by your traffic, not by surcharges.
What it costs
Prices are monthly and exclude VAT. The ports are guaranteed and unmetered, so the bill is the same at 1% and at 100% use.
| Part | Server | Per month |
|---|---|---|
| Origin | Dual Xeon E5 2699 v4, 256 GB RAM, 6 × 4 TB SSD, 1 Gbps | €578 |
| Three edges at 1 Gbps | Dual Xeon E5 2630 v3, 128 GB RAM, 1 Gbps, three countries | €627 |
| Three edges at 10 Gbps | Dual Xeon E5 2630 v3, 128 GB RAM, 10 Gbps, three countries | €1,977 |
What that works out to per terabyte depends on how busy the ports are. Traffic has peaks and quiet hours, so assume the ports average 30% of line rate over a month:
| Setup | Capacity | Delivered at 30% average | Total per month | Per TB delivered |
|---|---|---|---|---|
| Origin plus three 1 Gbps edges | 3 Gbps | about 292 TB | €1,205 | about €4.13 |
| Origin plus three 10 Gbps edges | 30 Gbps | about 2,916 TB | €2,555 | about €0.88 |
Compare those figures with the per GB price your CDN or cloud provider charges you today, and with what your traffic will be in a year. Our post on cloud egress fees against unmetered servers does that comparison in more detail. Keep in mind the hours you spend running it, which a commercial CDN does for you.
The edge: nginx as a cache
Each edge runs nginx as a caching reverse proxy. This configuration is a solid start:
# /etc/nginx/conf.d/edge.conf
proxy_cache_path /var/cache/nginx/edge levels=1:2 keys_zone=edge:256m
max_size=800g inactive=14d use_temp_path=off;
server {
listen 443 ssl;
server_name cdn.example.com;
ssl_certificate /etc/ssl/cdn/fullchain.pem;
ssl_certificate_key /etc/ssl/cdn/privkey.pem;
location / {
proxy_pass https://origin.example.com;
proxy_ssl_server_name on;
proxy_set_header Host origin.example.com;
proxy_cache edge;
proxy_cache_valid 200 301 302 7d;
proxy_cache_valid 404 1m;
proxy_cache_lock on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
add_header X-Cache $upstream_cache_status always;
}
}What the important lines do:
keys_zone=edge:256mkeeps the index of cached files in memory. One megabyte holds about 8,000 keys, so 256 MB indexes around two million files.max_size=800gcaps the cache on disk. Set it to what your drives hold, with some room to spare.proxy_cache_lock onmeans that when a hundred users ask for a new file at once, the origin receives one request, not a hundred.proxy_cache_use_stalekeeps serving the cached copy if the origin is down or slow, so a problem at the origin does not reach your users straight away.- The
X-Cacheheader shows HIT or MISS on every response, which is how you measure the cache hit ratio.
For large video files and downloads, add the slice module so an edge caches a file in pieces and can answer range requests without fetching the whole file first:
location /video/ {
slice 4m;
proxy_cache edge;
proxy_cache_key $host$uri$is_args$args$slice_range;
proxy_set_header Range $slice_range;
proxy_cache_valid 200 206 7d;
proxy_pass https://origin.example.com;
proxy_ssl_server_name on;
proxy_set_header Host origin.example.com;
}Most distribution packages of nginx include the slice module. Run
nginx -V and look for http_slice_module to be sure.Memory matters as much as disk. Linux keeps recently read files in RAM, so the most popular part of your catalogue is served from memory without touching the drives. That is why 128 GB of RAM on the entry server is useful on an edge, and why a 10 Gbps edge serving a large catalogue benefits from more SSDs. A 6 × 1 TB SSD set adds €69 a month.
Sending users to the nearest edge
GeoDNS looks at where a DNS query comes from and answers with the address of the closest edge. You can run it yourself, for example PowerDNS with its GeoIP backend, or use a DNS provider that offers geographic routing. Either way, add health checks so an edge that stops answering is removed from the replies, and keep the TTL low, around 60 seconds, so users move quickly when that happens. Our guide to redundant DNS across several locations covers running your own name servers.
Anycast is the other way to route users, with every edge announcing the same address. It needs your own address space and BGP sessions with each provider, which is a much bigger project. GeoDNS gets most of the benefit for a fraction of the work.
Certificates on every edge
Each edge needs a certificate for your CDN hostname. With GeoDNS, the HTTP check Let's Encrypt uses to validate a domain may reach a different edge than the one that asked for the certificate, because the validation runs from several places. Use DNS validation instead, or request the certificate on one machine and copy it to every edge with a deploy hook. Either way, renewals keep working without anyone logging in.
Purging, or how to avoid it
The simplest cache invalidation is never to need it. Put a version or a content hash in the file name, such as
app.3f9c2b.js or /v42/patch.bin, and publish a new name when the file changes. Old copies expire on their own. Keep short cache times only for the few paths that must change in place, like a playlist or a manifest.Protecting the edges and the origin
Every one of our dedicated servers sits behind 1 Tbps always on DDoS protection at layers 3 and 4, which covers volumetric floods. Floods of HTTP requests are your job: nginx
limit_req and limit_conn per address go a long way. Lock the origin down with a firewall so that only your edges can reach it, and it never has to face the public.A setup to start with
- Order the origin with enough SSD for your catalogue, and three edges in the countries closest to your users, on the dedicated servers page.
- Install nginx on the edges with the configuration above, and point it at the origin.
- Set up GeoDNS with health checks, and certificates with DNS validation.
- Move a small share of traffic first, watch the hit ratio in the
X-Cacheheader, then move the rest. - When an edge's busiest hour gets close to its port, upgrade the port. Our post on which port upgrade pays off shows the cheapest steps.