If you want servers that have been down for 30 days, then I thought it should be obvious you need max_over_time(up[30d]) == 0 ... but perhaps it isn't as obvious as I thought.
Let me break that query down into parts:
up[30d] : returns a *range vector* containing all data points for the timeseries with metric name "up" from T - 30 days to T (where T is the evaluation time, i.e. the point on the X axis)
By "timeseries" I mean distinct combination of metric name and labels, e.g.
up{instance="foo"}
up{instance="bar"}
are two different timeseries. They happen to share the same metric name ("up") but they are recording an independent sequence of measurements.
Think of the range vector as a two-dimensional grid: there are N different timeseries, each with M data points over that period. The data collected and stored in the TSDB might look like this:
up{instance="foo"} v1 . . . v2 . . . v3 . . .
up{instance="bar"} . . v4 . . . v5 . . . v6 .
-------------------------> time
Then:
max_over_time(...) : for each timeseries in the range vector, picks the highest value. This returns an *instant vector*, i.e. a single value for every timeseries, which is the maximum of each.
up{instance="foo"} v3
up{instance="bar"} v5
Each of those values is the maximum value of the timeseries, over the 30 day period.
Now, you've chosen to draw a graph of this expression, but it's important to realise that the graph itself doesn't need to be over 30 days. When you draw a graph of an expression, it will sweep across the evaluation time, evaluating the expression repeatedly at different instants in time over the given period.
Let's say, for example, you set the graph range to be 1 week, but you are graphing max_over_time(up[30d]) == 0
What will you get? This will be a series of points. Let's imagine the graph only had one point per day. Considering the position of each point on the time axis:
Aug 17: shows if the server has been down from (Aug 17 - 30 days) to (Aug 17)
Aug 16: shows if the server has been down from (Aug 16 - 30 days) to (Aug 16)
...
Aug 10: shows if the server has been down from (Aug 10 - 30 days) to (Aug 10)
In fact, for your purposes (asking, has the server been down for the *last 30 days*?) you don't need to draw a graph at all! In which case, if you turn on the "Instant" switch in Grafana it will only ask Prometheus to evaluate the expression for the current instant, which makes the query much faster and cheaper.
This is then an ideal query to use in a dashboard, where you just want to show a list of servers that have been down for the last 30 days. You don't care, for example, if 2 days ago they were down for the 30 days before that point, do you? Because that's what basically a graph of that expression will tell you: at each point in time, whether it was down for the previous 30 days.