SSD Nodes Learn 🎉 VPS from $5.50/mo
Guides Matt ConnorBy Matt Connor

VPS hosting in Frankfurt, Germany

Who should put a VPS in Frankfurt: DE-CIX peering, real latency numbers for German and EU users, and what hosting in the EU does and does not do for GDPR.

Who VPS hosting in Frankfurt is for

VPS hosting in Frankfurt fits projects whose users sit in Germany, in the wider German-speaking market, or spread across the European Union. Frankfurt is one of the places where European networks meet and hand traffic to each other directly, so a server there reaches most of the continent in a few tens of milliseconds. If your users are mostly in North America, a European server will feel slow to them however fast the machine is, because distance sets a floor that tuning cannot remove.

Two separate questions decide a location, and mixing them together is what produces bad choices. The first is where your users are, which is a question about distance and round-trip time. The second is where your data is allowed to live, which is a legal and contractual question. Frankfurt has a strong answer to the first for European audiences. For the second it removes one specific problem and settles nothing else.

Why is Frankfurt so well connected?

Frankfurt hosts DE-CIX (Deutsche Commercial Internet Exchange), an IXP (internet exchange point) that is among the largest in the world by peak traffic and by the number of connected networks. An IXP is a shared switching fabric inside a data centre where independent networks connect to each other, instead of paying a larger network to carry traffic between them. DE-CIX publishes its current traffic statistics on its own site, and those numbers change, so read them there rather than trusting a figure copied into an article.

The practical effect is about paths, not about totals. When your provider's network and your visitor's ISP (internet service provider) both connect at the same exchange, traffic between them crosses a single routed hop at that exchange. When they do not peer locally, the traffic has to reach some third network that carries both of them, and that network's nearest handover point may sit in another country. Two German networks exchanging traffic through Amsterdam or London pay the extra distance twice, once in each direction. Network engineers call this tromboning, and it is the usual reason a nearby server measures far away.

You can see it rather than assume it. Run mtr against your server from the network you care about and read the hop names in reverse DNS. Router hostnames usually embed IATA airport codes, so fra in a hop name means Frankfurt, ams means Amsterdam and lhr means London. A path from a German consumer connection to a German server that shows lhr in the middle is telling you exactly where the extra milliseconds went.

How far is Frankfurt from your users?

Light in fibre travels at roughly two thirds of its speed in a vacuum, close to 200,000 kilometres per second. A round trip covers the path twice, so the fastest possible round trip over a distance of d kilometres is d/100 milliseconds. That is a floor, and it is useful because nothing beats it.

ChartGreat-circle distance from Frankfurt and the round-trip floor it sets
The data behind this chart
[
  {
    "label": "Zurich",
    "distance_km": 304,
    "min_rtt_ms": 3.0
  },
  {
    "label": "Amsterdam",
    "distance_km": 365,
    "min_rtt_ms": 3.7
  },
  {
    "label": "Berlin",
    "distance_km": 424,
    "min_rtt_ms": 4.2
  },
  {
    "label": "Paris",
    "distance_km": 479,
    "min_rtt_ms": 4.8
  },
  {
    "label": "Milan",
    "distance_km": 519,
    "min_rtt_ms": 5.2
  },
  {
    "label": "Vienna",
    "distance_km": 600,
    "min_rtt_ms": 6.0
  },
  {
    "label": "London",
    "distance_km": 640,
    "min_rtt_ms": 6.4
  },
  {
    "label": "Warsaw",
    "distance_km": 903,
    "min_rtt_ms": 9.0
  },
  {
    "label": "Stockholm",
    "distance_km": "1,197",
    "min_rtt_ms": 12.0
  },
  {
    "label": "Madrid",
    "distance_km": "1,419",
    "min_rtt_ms": 14.2
  },
  {
    "label": "New York",
    "distance_km": "6,206",
    "min_rtt_ms": 62.1
  }
]

These are computed from straight-line distance, not measured. Read the last column as the best case that physics allows. Real measurements usually land between 1.5 and 2 times the floor, because fibre follows roads and river valleys rather than great circles, and because every router on the path adds a small forwarding and queuing delay.

Berlin sits 424 km from Frankfurt, a floor of 4.2 ms. Madrid is 1,419 km out, a floor of 14.2 ms, and it is the far corner of the EU from here. New York is 6,206 km away with a floor of 62.1 ms, which is why a transatlantic audience is a location decision and not a tuning problem.

What does a slow round trip cost a page load?

One round trip is rarely one round trip. Opening an HTTPS connection costs one round trip for the TCP (transmission control protocol) handshake and one more for the TLS (transport layer security) 1.3 handshake. The request then costs a third before the first byte of the response comes back. TLS 1.2 adds a fourth. A DNS (domain name system) lookup that is not already cached adds at least one more, to a different server again.

ChartTime to first byte modelled from round-trip time, three round trips
The data behind this chart
[
  {
    "label": "User in Frankfurt",
    "rtt_ms": 5,
    "first_byte_ms": 15
  },
  {
    "label": "User in Warsaw",
    "rtt_ms": 20,
    "first_byte_ms": 60
  },
  {
    "label": "User in Madrid",
    "rtt_ms": 30,
    "first_byte_ms": 90
  },
  {
    "label": "User in New York",
    "rtt_ms": 90,
    "first_byte_ms": 270
  },
  {
    "label": "User in Singapore",
    "rtt_ms": 170,
    "first_byte_ms": 510
  }
]

The round-trip column here is an assumption about a plausible path to a Frankfurt server, and the second column is arithmetic on it: three round trips before the first byte. A user in Frankfurt waits 15 ms. A user in Singapore, at 170 ms of round-trip time, waits 510 ms for the same response, before the browser has drawn anything.

The multiplier is the whole point. Each extra millisecond of RTT (round-trip time) costs about three milliseconds before the first byte, and then it keeps costing. The HTML names a stylesheet, the stylesheet names a font, and each of those discoveries is another round trip on the same connection. Adding a couple of hundred milliseconds of distance turns a page that felt instant into a page that feels slow, while the server does exactly the same work in exactly the same time.

This also sets the limit of what a CDN (content delivery network) fixes. Static files served from a cache near the user skip the long path. A logged-in dashboard that has to ask your database a question does not: that request still crosses the whole distance twice. Putting the origin near the people who log in is the part no cache does for you.

How do I measure this from where my users are?

Run these from a machine on the network you care about, ideally a home or office connection in the country you serve. Measuring from another server in another data centre tells you about data centre paths, not about your users. The commands below are examples to run yourself: the only latency figures worth acting on are the ones you measured.

ping -c 20 your-server.example.com

The summary line reads rtt min/avg/max/mdev = .... Read avg for the typical case and mdev for jitter, the variation between packets. A normal avg with a high mdev means the path is unstable, and that hurts interactive work such as SSH or voice more than a slightly higher average does.

sudo apt update && sudo apt install -y mtr-tiny
mtr -rwzc 50 your-server.example.com

-r prints a report instead of the live display, -w keeps long hostnames intact, -z shows the AS (autonomous system) number of each hop, and -c 50 sends fifty cycles. Loss shown at one middle hop, with no loss at the final hop, is normal and not a fault: many routers rate-limit the ICMP replies they generate for themselves while forwarding everything else fine. Loss that starts at a hop and continues to every hop after it is real loss.

curl -sS -o /dev/null -w 'dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' https://your-server.example.com/

Each field is cumulative seconds since the request started, so you read it by subtraction. time_namelookup is DNS. time_connect minus that is the TCP handshake, close to one round trip. time_appconnect minus time_connect is the TLS handshake. time_starttransfer minus time_appconnect is your application's own thinking time plus one more round trip. If the gaps are small and total is still large, the problem is your code, not the city.

For throughput rather than latency, run iperf3 -s on the VPS, open its port on the firewall, and run iperf3 -c your-server.example.com -R from the client to test the download direction. To measure from places you do not have a machine, RIPE Atlas gives you probes across Europe. When you compare two servers rather than two networks, use a fixed method instead of one-off numbers, which is what a repeatable VPS benchmark is for.

Does a server in Frankfurt make my project GDPR compliant?

No, and the reason is worth stating precisely. The GDPR (General Data Protection Regulation) applies based on whose personal data you process and where your organisation is established, not on the country the hardware stands in. Moving a server to Frankfurt does not create compliance, and running one outside the EU does not automatically break it. Location is one input among several.

What hosting inside the EU or the wider EEA (European Economic Area) does remove is the international transfer question. The regulation has a whole chapter on sending personal data outside the EEA, which needs a legal instrument such as an adequacy decision or standard contractual clauses. Data that stays in Frankfurt is not being transferred, so that chapter does not apply to that hop. That is a genuine simplification, and it is the honest size of the benefit.

Everything else stays your work. You still need a lawful basis for each purpose, working access and deletion rights for the people in your database, a retention limit you actually enforce, security measures appropriate to the risk, and a report to the supervisory authority within 72 hours of becoming aware of a personal data breach. You also need a processor agreement with your hosting provider, known in Germany as an Auftragsverarbeitungsvertrag or AVV. Note too that a server in Frankfurt can still involve a transfer if support staff outside the EEA can access it, so check who holds the keys.

Germany adds its own layer on top: the federal BDSG (Bundesdatenschutzgesetz) supplements the regulation with national rules, and employee data is the area that surprises people most often. This section is general background, not legal advice. The European Data Protection Board publishes the official guidelines at edpb.europa.eu, and anything with real consequences deserves a qualified adviser rather than a tutorial.

What should I change on the server itself?

Keep the system clock on UTC (coordinated universal time) and format timestamps in your application. Germany observes daylight saving, so local time moves by an hour twice a year, and one hour in late October repeats itself. Logs written in local time contain two 02:30 entries on that night, and correlating them across regions turns into guesswork. If you still want local time on the box, set it explicitly and check it:

sudo timedatectl set-timezone Europe/Berlin
timedatectl

The output should show Time zone: Europe/Berlin (CEST, +0200) in summer and +0100 in winter.

German text sorts wrongly under the default C locale, because C sorting compares raw bytes. Generate the locale and watch the difference:

sudo locale-gen de_DE.UTF-8
sudo update-locale
printf 'Zebra\nÄpfel\nApfel\n' | LC_ALL=C sort
printf 'Zebra\nÄpfel\nApfel\n' | LC_ALL=de_DE.UTF-8 sort

The first sort puts Äpfel after Zebra, because its first byte in UTF-8 is higher than any ASCII letter. The second puts it next to Apfel, where a German reader expects it. This matters more than it looks, since PostgreSQL and MySQL fix a collation when the database is created, and changing it later means rebuilding indexes. Decide before you load data.

A German package mirror shortens apt runs. On Ubuntu 24.04 the sources live in /etc/apt/sources.list.d/ubuntu.sources in deb822 format, so change the URIs: line to http://de.archive.ubuntu.com/ubuntu/ rather than adding a second file. Adding one gives you Target Packages ... is configured multiple times, which is the deb822 duplicate sources error and stops updates until you resolve it.

Publish an AAAA record. Some German ISPs hand consumer connections a DS-Lite (dual-stack lite) setup, where the customer has no public IPv4 address at all and their IPv4 traffic crosses the carrier's translation gateway. That gateway adds latency and congests at peak hours, while IPv6 traffic goes straight out. Check both paths after you set the record:

dig AAAA your-server.example.com +short
curl -6 -sS -o /dev/null -w '%{http_code}\n' https://your-server.example.com/

A 200 from the second command means IPv6 works end to end. Could not resolve host or a connection error means the record or the listener is missing, and your DS-Lite visitors are taking the slow path.

When Frankfurt is the wrong choice

  • Your users are in the United States. Serve them from there: a VPS in Dallas sits near the centre of the country, and VPS hosting in New York is the shorter path for the east coast and for traffic that crosses the Atlantic anyway.
  • Your users are in Latin America. Frankfurt is further from São Paulo than it is from New York, so a VPS in Brazil is the honest answer for that audience.
  • Your data must stay inside a specific country outside the EU. Canadian public sector work is the common case, and what actually matters for Canadian VPS hosting covers residency there.
  • You run a game server. Players feel every millisecond of round-trip time, so proximity to them outranks every other specification: choosing a VPS for game servers works through it.

For a European audience spread across several countries, Frankfurt is the safe single choice, and it stays safe as you grow, because the networks you need to reach are already at the exchange. Measure from where your users are before you move and again after, and keep both sets of numbers.

FAQ

Is one VPS in Frankfurt enough for the whole of Europe?

For most projects, yes. Straight-line distance puts the floor at 12.0 ms to Stockholm and 14.2 ms to Madrid, and real paths run about 1.5 to 2 times their floor, so almost the whole EU stays within a low tens of milliseconds of one Frankfurt server. Add a second location when you have measured a real complaint from a specific country, or when you need failover rather than speed.

Does hosting in Frankfurt make my project GDPR compliant?

No. The GDPR applies based on whose personal data you process and where you are established, not on where the server is. Hosting in the EU removes the international transfer question for that hop, which is a real simplification and the whole of the benefit. You still need a lawful basis, working data subject rights, a retention limit, security measures, breach reporting within 72 hours, and a processor agreement with your provider, called an AVV in Germany. This is general information, not legal advice.

How much latency should I expect between Frankfurt and Berlin?

The two cities are 424 km apart, which sets a hard floor of 4.2 ms of round-trip time. A well-peered path typically measures 1.5 to 2 times its floor. Confirm it with ping -c 20 your-server.example.com from a connection in Berlin and read the avg value in the rtt min/avg/max/mdev line. A result far above that range usually means the traffic left Germany and came back, which mtr -rwzc 50 will show you in the hop names.

Should I set my Frankfurt server's timezone to Europe/Berlin?

Usually no. Keep the system on UTC so that logs stay comparable and no timestamp is ambiguous. Germany switches to CEST in spring and back to CET in autumn, and on the autumn night one local hour occurs twice, so two different events can carry the same local timestamp. Format times in the local zone in your application, where you have the context to do it correctly. If you do want the whole box on local time, run sudo timedatectl set-timezone Europe/Berlin and verify with timedatectl.

Will an IPv4-only server be a problem for German visitors?

It will work, but it is slower for some of them. Several German ISPs give consumer connections a DS-Lite setup with no public IPv4 address, so those customers reach an IPv4-only server through the carrier's translation gateway, which adds latency and congests at busy times. Publishing an AAAA record and listening on IPv6 gives them a direct path. Test it with dig AAAA your-server.example.com +short and a curl -6 request, and expect an HTTP 200 from both address families.

#frankfurt#germany#europe#latency#gdpr