Before getting into the debugging, I want to make one thing clear: I actually liked FDCServers. This is not a review telling people to avoid them, and it is not an attempt to judge their infrastructure as a whole. I had one VPS, during one period of time, with one unusually difficult problem.
For quite a while, that VPS did exactly what I wanted and handled real production traffic normally. Then something changed. Ordinary requests started taking an absurd amount of time. Pages opened slowly, then sometimes stopped opening at all. At other moments, the machine looked normal again before I could inspect the failure properly.
At the worst point I captured:
CPU iowait: 97–100%
I/O PSI full: ~95–98%
read latency: up to 18.7 seconds
flush latency: up to 53.6 seconds
I/O queue depth: 128+
Nginx was running. My backend was running. The VM was online. And almost nothing useful was happening.
The production failure was frustrating, but the debugging itself was genuinely enjoyable. I love Linux for exactly this kind of problem: a machine can look alive from the outside while several independent kernel interfaces quietly show where useful progress has stopped. This article is about following those clues, not deciding whether FDCServers is a good or bad provider.
active (running) turned out to mean almost nothing
I started with the obvious checks:
top
free -h
df -h
systemctl status nginx
During healthy periods I could see roughly:
D-state processes: 0
CPU iowait: ~0%
disk latency: ~2–4 ms
I/O PSI some: 0.04
I/O PSI full: 0.04
If I had connected only then, I could easily have concluded that the VPS was healthy. Then normal traffic returned and the state could change completely. A healthy snapshot of an intermittent system tells you very little about its unhealthy state. I needed to collect evidence while the failure was actually happening.
The iostat sample that changed the investigation
r_await = 18744 ms
f_await = 53561 ms
aqu-sz = 128.54
util ≈ 100%
read = 88 KB/s
Completed reads were taking about 18.7 seconds. Flush operations were taking about 53.6 seconds. The average queue was above 128, yet useful read throughput was only 88 KB/s.
This did not look like storage simply working hard because it was efficiently serving a demanding workload. The virtual block path was effectively saturated while accomplishing very little. High utilization alone is not a failure, but high utilization combined with enormous latency, a large queue, stalled tasks, tiny throughput, and failing requests is a very different signal.
I stopped treating iowait as a diagnosis
blocked processes: 4–9
CPU iowait: 97–100%
CPU idle: 0%
It is tempting to summarize that as the CPU spending 100% of its time waiting for the disk. That intuition is useful, but Linux accounting is more complicated. iowait is not a direct measurement of the disk itself, so I did not build the diagnosis around one percentage. I treated it as one symptom and looked for independent evidence.
PSI showed that I/O was stopping useful work
cat /proc/pressure/io
During one severe period:
some avg10=99.14
full avg10=95.55
In later reproductions, full approached 98%. For I/O pressure, some represents time when at least some non-idle work is stalled on I/O, while full represents time when all non-idle tasks are simultaneously stalled on I/O. A value near 100% says much more than simply saying the disk is busy: the workload is barely getting opportunities to make progress.
D-state told me to stop blaming one process
ps -eo state,pid,ppid,etime,wchan:50,comm,args | awk 'NR==1 || $1 ~ /^D/'
Seeing one process briefly in D-state does not prove a storage problem. What mattered was which processes were blocked together. I observed unrelated components including jbd2, systemd-journald, Nginx workers, Nginx cache processes, and other filesystem activity.
If only my backend is stuck, I investigate the backend. If Nginx alone is stuck, I investigate Nginx. But when Nginx, the system journal, and the EXT4 journal thread all stop progressing together, their shared dependency becomes much more interesting. In this case, that dependency was the filesystem and the storage path below it.
The kernel stacks gave me the next layer
The EXT4 journal thread appeared in paths such as:
wait_on_buffer
jbd2_log_wait_commit
jbd2_journal_commit_transaction
Nginx workers appeared in ordinary filesystem-read paths:
folio_wait_bit_common
filemap_read
generic_file_read_iter
ext4_file_read_iter
vfs_read
pread64
At one point the kernel reported:
INFO: task nginx blocked for more than 122 seconds.
systemctl status nginx could still say active (running). Both observations were true: the process existed, but an Nginx task had spent more than two minutes unable to complete useful work. A running process and a healthy service are not the same thing.
My cleanest experiment took about three milliseconds
A request through Nginx failed:
HTTP=000
SSL connection timeout
Then I bypassed Nginx and contacted the local application backend directly:
connect = 0.000423 s
TTFB = 0.003063 s
total = 0.003139 s
About 3 milliseconds. The exact HTTP status was irrelevant for this test. The backend accepted the connection, executed the request, and returned a response almost immediately. At approximately the same time, Nginx workers were visible inside EXT4 read paths.
application execution → progressing normally
filesystem-backed web path → not progressing normally
The deeper I went, the less plausible an application-level explanation became.
The same VPS could collapse in about 30 seconds
Before one reproduction:
HTTP: 200
D-state: 0
CPU iowait: 3%
r_await: ~1.18 ms
I/O PSI full: ~2.95%
Roughly half a minute later:
D-state: 4
CPU iowait: 91%
CPU idle: 0%
I/O PSI some: 86.11%
I/O PSI full: 78.02%
r_await: 236.50 ms
HTTP: 000
Later it deteriorated further:
CPU iowait: 96–100%
I/O PSI full: ~98%
HTTPS queue: 512
HTTP: 000
When I removed the workload, the opposite transition could happen quickly:
D-state: 0
CPU iowait: 6%
r_await: ~0.98 ms
queue depth: ~0.07
HTTP: 200
This is why intermittent infrastructure failures are so difficult. Someone can inspect the same VM ten minutes later and honestly report sub-millisecond disk latency. They are not necessarily wrong; they are simply looking at a different state.
A zero latency value can be surprisingly unhelpful
I also saw an iostat interval with r_await = 0 while the system was clearly unhealthy: high iowait, processes in D-state, outstanding I/O, almost no completed reads, and almost no throughput.
Averages based on completed operations become less informative when almost nothing completes during the sampling interval. A zero does not necessarily prove that reads completed instantly; there may simply be too few useful completions to describe the operations that remain stuck.
After this incident, I stopped reading storage metrics one field at a time. I want to see latency, IOPS, throughput, queue depth, in-flight I/O, D-state, PSI, and actual request completion together.
One incident became a much longer support investigation
The first severe event overlapped with a scheduled backup on the original infrastructure, and FDCServers confirmed that the backup was running. That was a reasonable candidate explanation. But I later reproduced the same class of storage stall after the backup had finished and outside the original backup window.
The problem returned on multiple days. During one incident, the VPS later became unavailable for 4 hours, 41 minutes and 15 seconds according to my service logs. I cannot prove that the storage stall itself caused the VM to enter that state; proving that would require host-side information I did not have.
application
↓
Linux VFS
↓
EXT4
↓
virtual block device
↓
?
Behind that question mark may be virtualization, host queues, storage networking, distributed storage, physical media, schedulers, and other systems invisible to the guest. I could see where the failure manifested, but not the physical root cause.
FDCServers escalated the issue internally and eventually migrated the VPS to a different node. I later captured another severe guest-side storage stall after that migration. That does not prove that every FDCServers node had a storage problem. It proves only that, from my point of view, the problem affecting my VPS had not been eliminated.
Why I still do not consider this a negative FDCServers story
Infrastructure incidents are easy to turn into verdicts about an entire provider. I do not want to do that here.
I distinguish between a service whose normal operating model is fundamentally incompatible with my workload and an infrastructure problem that appears intermittently, is difficult to reproduce, and takes a long time to isolate. My FDCServers experience felt like the second case.
The VPS had worked normally before the incident and carried real production traffic. Support investigated the problem and tried to resolve it. Eventually I had enough evidence to decide that I no longer wanted production traffic depending on that particular VPS.
I asked FDCServers to cancel the service and refund me. They refunded me. That matters to my overall impression.
I have not retested their current infrastructure, so I cannot tell you how an FDCServers VPS behaves today. I also have no evidence that what happened to my instance was representative of their fleet. Infrastructure changes constantly. I would not turn one difficult incident from one VPS into a permanent statement about an entire provider. And I am not recommending FDCServers either. I am simply describing what happened to me.
The part I enjoyed most was Linux itself
The downtime was frustrating, but the investigation was fun. I genuinely enjoyed finding the boundary of the problem.
I never had enough visibility to identify the physical root cause. The answer I wanted was simpler: where does useful work stop?
The backend responded in about three milliseconds. Nginx said it was active, while kernel stacks showed it waiting inside EXT4 reads. vmstat showed blocked processes and extreme I/O wait. PSI showed that I/O stalls consumed almost the entire workload. iostat showed enormous latency and queueing. D-state showed unrelated processes waiting together. The kernel even reported an Nginx task blocked for more than 122 seconds.
No single metric solved the incident. The agreement between them did. That is one of the reasons I love Linux: you can begin with something vague like “my website sometimes does not open” and gradually turn it into a precise statement about the layer where useful work stops progressing.
The debugging workflow I use now
top
free -h
df -h
date -u
uptime
cat /proc/pressure/io
cat /proc/pressure/memory
cat /proc/pressure/cpu
vmstat 1 10
iostat -x 1 10
ps -eo state,pid,ppid,etime,wchan:50,comm,args | awk 'NR==1 || $1 ~ /^D/'
ss -lntp
journalctl -k --since "30 min ago" --no-pager
Whenever possible, I also test each part of the request path independently:
public request
↓
reverse proxy
↓
direct backend
↓
filesystem
↓
block device
Instead of asking only “why is the server slow?”, I now ask: at which layer does useful work stop completing? That question produces much better experiments.
One final rule: collect the evidence before you reboot
A reboot may be exactly what production needs, but it can also erase the most valuable state you are ever going to see.
Before reboot:
D-state: high
I/O PSI: ~97%
iowait: ~100%
queues: large
requests: failing
After reboot:
D-state: 0
latency: milliseconds
requests: healthy
When availability and business impact allow it, I first capture the UTC timestamp, PSI, vmstat, iostat, D-state and wchan, kernel messages, socket queues, and request timings. Then I recover the machine, not the other way around.
The server was running. The workload was not.
I never learned which host-side component ultimately caused the incident. I cannot tell you that a particular SSD failed, identify a specific storage node, or prove what happened behind the virtual block device.
What I could establish from inside Linux was enough:
read latency: up to 18.7 s
flush latency: up to 53.6 s
I/O PSI full: almost 100%
iowait: almost 100%
I/O queue: 128+
Nginx: blocked in filesystem reads
EXT4/jbd2: blocked waiting for I/O
direct backend: ~3 ms
HTTP through Nginx: timing out
That was enough to separate my application from the failing layer and enough to make an operational decision. FDCServers refunded the VPS, I moved on, and I do not hold one difficult infrastructure incident up as a verdict on the provider.
What stayed with me was something more useful: a process can be running, a service can be active, a VM can be online, ping can work, and the machine can still be accomplishing almost no useful work.
Linux gives you enough evidence to tell the difference. You just have to ask the system the right questions while the failure is still there.