diff --git a/spring-grpc-docs/README.md b/spring-grpc-docs/README.md
new file mode 100644
index 00000000..c7160666
--- /dev/null
+++ b/spring-grpc-docs/README.md
@@ -0,0 +1,16 @@
+# Spring gRPC Docs
+
+## Configuration Properties
+The Spring gRPC configuration properties are automatically documented as follows:
+
+1. This module contains a Java class (`org.springframework.grpc.internal.ConfigurationPropertiesAsciidocGenerator`) that is compiled when the module is built.
+1. This class is then used during the Maven `package` phase to generate an asciidoc page containing each of the configuration properties.
+1. The asciidoc is then included in the Antora reference documentation.
+
+## Antora Site
+
+To build the Antora site locally run the following command from the project root directory:
+```
+./mvnw -pl spring-grpc-docs antora
+```
+You can then view the output by opening `spring-grpc-docs/target/antora/site/index.html`.
diff --git a/spring-grpc-docs/pom.xml b/spring-grpc-docs/pom.xml
index 8a7601ee..1e2171f4 100644
--- a/spring-grpc-docs/pom.xml
+++ b/spring-grpc-docs/pom.xml
@@ -12,6 +12,27 @@
Spring gRPC Docs
Spring gRPC documentation
+
+ 3.4.1
+ ${project.basedir}/src/main/antora/modules/ROOT/partials/_configprops.adoc
+ spring.grpc.*
+
+
+
+
+ org.springframework
+ spring-core
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ org.springframework.grpc
+ spring-grpc-spring-boot-autoconfigure
+ ${project.version}
+
+
@@ -63,7 +84,30 @@
true
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ ${exec-maven-plugin.version}
+
+
+ generate-configprops
+ package
+
+ java
+
+
+
+
+ true
+ false
+ org.springframework.grpc.internal.ConfigurationPropertiesAsciidocGenerator
+
+ ${configprops.path}
+ ${configprops.inclusionPattern}
+
+
+
-
\ No newline at end of file
+
diff --git a/spring-grpc-docs/src/main/antora/modules/ROOT/nav.adoc b/spring-grpc-docs/src/main/antora/modules/ROOT/nav.adoc
index 09a9ee3d..67b8ced3 100644
--- a/spring-grpc-docs/src/main/antora/modules/ROOT/nav.adoc
+++ b/spring-grpc-docs/src/main/antora/modules/ROOT/nav.adoc
@@ -1,6 +1,5 @@
* xref:index.adoc[Overview]
* xref:concepts.adoc[GRPC Concepts]
* xref:getting-started.adoc[Getting Started]
-
* xref:contribution-guidelines.adoc[Contribution Guidelines]
-
+* xref:appendix.adoc[]
diff --git a/spring-grpc-docs/src/main/antora/modules/ROOT/pages/appendix.adoc b/spring-grpc-docs/src/main/antora/modules/ROOT/pages/appendix.adoc
new file mode 100644
index 00000000..c88d1b88
--- /dev/null
+++ b/spring-grpc-docs/src/main/antora/modules/ROOT/pages/appendix.adoc
@@ -0,0 +1,11 @@
+:numbered!:
+
+[appendix]
+[[common-application-properties]]
+= Common application properties
+:page-section-summary-toc: 1
+
+Various properties can be specified inside your `application.properties` file, inside your `application.yml` file, or as command line switches.
+This appendix provides a list of common `Spring gRPC` properties.
+
+include::partial$_configprops.adoc[]
diff --git a/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc b/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc
new file mode 100644
index 00000000..4916a7b2
--- /dev/null
+++ b/spring-grpc-docs/src/main/antora/modules/ROOT/partials/_configprops.adoc
@@ -0,0 +1,18 @@
+|===
+|Name | Default | Description
+
+|spring.grpc.client.channels | |
+|spring.grpc.server.address | `+++*+++` | Server address to bind to. The default is any IP address ('*').
+|spring.grpc.server.keep-alive.max-age | | Maximum time a connection may exist before being gracefully terminated (default infinite).
+|spring.grpc.server.keep-alive.max-age-grace | | Maximum time for graceful connection termination (default infinite).
+|spring.grpc.server.keep-alive.max-idle | | Maximum time a connection can remain idle before being gracefully terminated (default infinite).
+|spring.grpc.server.keep-alive.permit-time | `+++5m+++` | Maximum keep-alive time clients are permitted to configure (default 5m).
+|spring.grpc.server.keep-alive.permit-without-calls | `+++false+++` | Whether clients are permitted to send keep alive pings when there are no outstanding RPCs on the connection (default false).
+|spring.grpc.server.keep-alive.time | `+++2h+++` | Duration without read activity before sending a keep alive ping (default 2h).
+|spring.grpc.server.keep-alive.timeout | `+++20s+++` | Maximum time to wait for read activity after sending a keep alive ping. If sender does not receive an acknowledgment within this time, it will close the connection (default 20s).
+|spring.grpc.server.max-inbound-message-size | `+++4194304B+++` | Maximum message size allowed to be received by the server (default 4MiB).
+|spring.grpc.server.max-inbound-metadata-size | `+++8192B+++` | Maximum metadata size allowed to be received by the server (default 8KiB).
+|spring.grpc.server.port | `+++9090+++` | Server port to listen on. When the value is 0, a random available port is selected. The default is 9090.
+|spring.grpc.server.shutdown-grace-period | `+++30s+++` | Maximum time to wait for the server to gracefully shutdown. When the value is negative, the server waits forever. When the value is 0, the server will force shutdown immediately. The default is 30 seconds.
+
+|===
\ No newline at end of file
diff --git a/spring-grpc-docs/src/main/java/org/springframework/grpc/internal/ConfigurationPropertiesAsciidocGenerator.java b/spring-grpc-docs/src/main/java/org/springframework/grpc/internal/ConfigurationPropertiesAsciidocGenerator.java
new file mode 100644
index 00000000..a5670e10
--- /dev/null
+++ b/spring-grpc-docs/src/main/java/org/springframework/grpc/internal/ConfigurationPropertiesAsciidocGenerator.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2012-2024 the original author or authors.
+ *
+ * Licensed 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 org.springframework.grpc.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.util.StreamUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Generate Asciidoc for configuration properties.
+ *
+ * Copied from 'spring-cloud-build' to avoid direct dependency on Spring Cloud.
+ *
+ * @author Marcin Grzejszczak
+ * @author Chris Bono
+ */
+public class ConfigurationPropertiesAsciidocGenerator {
+
+ public static void main(String... args) {
+ String outputFile = args[0];
+ String inclusionPattern = args.length > 1 ? args[1] : ".*";
+ File parent = new File(outputFile).getParentFile();
+ if (!parent.exists()) {
+ System.out.println("No parent directory [" + parent.toString()
+ + "] found. Will not generate the configuration properties file");
+ return;
+ }
+ new Generator().generate(outputFile, inclusionPattern);
+ }
+
+ static class Generator {
+
+ void generate(String outputFile, String inclusionPattern) {
+ try {
+ System.out.println("Parsing all configuration metadata");
+ Resource[] resources = getResources();
+ System.out.println("Found [" + resources.length + "] configuration metadata jsons");
+ TreeSet names = new TreeSet<>();
+ Map descriptions = new HashMap<>();
+ final AtomicInteger count = new AtomicInteger();
+ final AtomicInteger matchingPropertyCount = new AtomicInteger();
+ final AtomicInteger propertyCount = new AtomicInteger();
+ Pattern pattern = Pattern.compile(inclusionPattern);
+ for (Resource resource : resources) {
+ if (resourceNameContainsPattern(resource)) {
+ count.incrementAndGet();
+ byte[] bytes = StreamUtils.copyToByteArray(resource.getInputStream());
+ Map response = new ObjectMapper().readValue(bytes, HashMap.class);
+ List