diff --git a/core/src/main/java/com/linecorp/armeria/server/healthcheck/DiskMemoryHealthChecker.java b/core/src/main/java/com/linecorp/armeria/server/healthcheck/DiskMemoryHealthChecker.java
new file mode 100644
index 00000000000..06f81b0c7e7
--- /dev/null
+++ b/core/src/main/java/com/linecorp/armeria/server/healthcheck/DiskMemoryHealthChecker.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024 LY Corporation
+ *
+ * LY Corporation licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.linecorp.armeria.server.healthcheck;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import java.io.File;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.function.Supplier;
+
+/**
+ * A {@link HealthChecker} that reports as unhealthy
+ * when the target free disk space exceeds a file disk usable space.
+ * For example:
+ *
{@code
+ * final DiskMemoryHealthChecker diskMemoryHealthChecker = HealthChecker.ofDisk(0.8, new File("/tmp"));
+ *
+ * // Returns false if a file disk usable memory space is less than 80%,
+ * // or true if a file disk usable memory space is greater than or equal to 80%.
+ * final boolean healthy = diskMemoryHealthChecker.isHealthy();
+ * }
+ */
+// Forked from DiskSpaceMetrics.java in the micrometer core.
+final class DiskMemoryHealthChecker implements Supplier> {
+
+ private final double freeDiskSpacePercentage;
+
+ private final File path;
+
+ DiskMemoryHealthChecker(double freeDiskSpacePercentage, File path) {
+ checkArgument(freeDiskSpacePercentage >= 0 && freeDiskSpacePercentage <= 1,
+ "freeDiskSpacePercentage: %s (expected >= 0 and expected <= 1)", freeDiskSpacePercentage);
+ requireNonNull(path);
+ this.freeDiskSpacePercentage = freeDiskSpacePercentage;
+ this.path = path;
+ }
+
+ /**
+ * Returns true if the file usable space is greater or equal than the target space.
+ * @return boolean
+ */
+ public boolean isHealthy() {
+ return freeDiskSpacePercentage * path.getTotalSpace() <= path.getUsableSpace();
+ }
+
+ /**
+ * Returns a {@link CompletionStage} that represents
+ * the health of the file disk usable space asynchronously.
+ * @return CompletionStage
+ */
+ @Override
+ public CompletionStage get() {
+ return CompletableFuture.supplyAsync(() -> new HealthCheckStatus(isHealthy(), 500));
+ }
+}
diff --git a/core/src/main/java/com/linecorp/armeria/server/healthcheck/HealthChecker.java b/core/src/main/java/com/linecorp/armeria/server/healthcheck/HealthChecker.java
index 61e5f84d84a..24f69c9ff53 100644
--- a/core/src/main/java/com/linecorp/armeria/server/healthcheck/HealthChecker.java
+++ b/core/src/main/java/com/linecorp/armeria/server/healthcheck/HealthChecker.java
@@ -19,6 +19,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
+import java.io.File;
import java.time.Duration;
import java.util.concurrent.CompletionStage;
import java.util.function.Supplier;
@@ -105,6 +106,22 @@ static HealthChecker ofCpu(double targetSystemCpuUsage, double targetProcessCpuU
return new CpuHealthChecker(targetSystemCpuUsage, targetProcessCpuUsage);
}
+ /**
+ * Creates a new instance of {@link ListenableHealthChecker} which reports health based on free disk space
+ * of the file system the specified path belongs to.
+ * There must be more free space (inclusive) than the specified threshold in order
+ * for the {@link ListenableHealthChecker} to report as healthy.
+ *
+ * @param freeDiskSpacePercentage the target free disk space as a percentage (0 - 1).
+ * @param path the path to the file system to check.
+ * @return an instance of {@link ListenableHealthChecker} configured with
+ * the provided disk memory space targets.
+ */
+ static ListenableHealthChecker ofDisk(double freeDiskSpacePercentage, File path) {
+ return new ScheduledHealthChecker(new DiskMemoryHealthChecker(freeDiskSpacePercentage, path),
+ Duration.ofSeconds(5), CommonPools.workerGroup().next());
+ }
+
/**
* Returns {@code true} if and only if the {@link Server} is healthy.
*/
diff --git a/core/src/test/java/com/linecorp/armeria/server/healthcheck/DiskMemoryCheckerTest.java b/core/src/test/java/com/linecorp/armeria/server/healthcheck/DiskMemoryCheckerTest.java
new file mode 100644
index 00000000000..b012d839029
--- /dev/null
+++ b/core/src/test/java/com/linecorp/armeria/server/healthcheck/DiskMemoryCheckerTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2024 LY Corporation
+ *
+ * LY Corporation licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.linecorp.armeria.server.healthcheck;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Test;
+
+class DiskMemoryCheckerTest {
+
+ @Test
+ void throwExceptionWhenTargetFreeDiskSpaceLowerThanZero() {
+ final double targetFreeDiskSpace = -1.0;
+ assertThatThrownBy(() -> HealthChecker.ofDisk(targetFreeDiskSpace, new File("/tmp")))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("freeDiskSpacePercentage: %.1f (expected >= 0 and expected <= 1)",
+ targetFreeDiskSpace);
+ }
+
+ @Test
+ void throwExceptionWhenPathIsNull() {
+ final double targetFreeDiskSpace = 1.0;
+ assertThatThrownBy(() -> HealthChecker.ofDisk(targetFreeDiskSpace, null))
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ void shouldReturnTrueWhenDiskSpaceIsHealthy() {
+ final DiskMemoryHealthChecker diskMemoryHealthChecker = new DiskMemoryHealthChecker(0.8, new File(""));
+ assertThat(diskMemoryHealthChecker.isHealthy()).isTrue();
+ }
+}