diff --git a/deploy/local/launchctl/README.md b/deploy/local/launchctl/README.md
index f47f21ce..0e9ca21e 100644
--- a/deploy/local/launchctl/README.md
+++ b/deploy/local/launchctl/README.md
@@ -34,6 +34,7 @@ export VEHICLE_IDENTITY_MYSQL_REFRESH_INTERVAL='60s'
export MYSQL_JDBC_URL="$VEHICLE_IDENTITY_MYSQL_JDBC_URL"
export MYSQL_USERNAME="$VEHICLE_IDENTITY_MYSQL_USERNAME"
export MYSQL_PASSWORD="$VEHICLE_IDENTITY_MYSQL_PASSWORD"
+export KAFKA_TOPIC_HISTORY_DLQ='vehicle.dlq.history.v1'
export YUTONG_MQTT_ENABLED='false'
export YUTONG_MQTT_URI=''
export YUTONG_MQTT_TOPIC='#'
diff --git a/deploy/local/launchctl/com.lingniu.vehicle-history.plist.template b/deploy/local/launchctl/com.lingniu.vehicle-history.plist.template
index 41d8bc1f..d3b82d2f 100644
--- a/deploy/local/launchctl/com.lingniu.vehicle-history.plist.template
+++ b/deploy/local/launchctl/com.lingniu.vehicle-history.plist.template
@@ -40,6 +40,8 @@
vehicle.event.mqtt-yutong.v1
KAFKA_TOPIC_YUTONG_MQTT_RAW
vehicle.raw.mqtt-yutong.v1
+ KAFKA_TOPIC_HISTORY_DLQ
+ __KAFKA_TOPIC_HISTORY_DLQ__
TDENGINE_HISTORY_ENABLED
true
TDENGINE_HISTORY_DATABASE
diff --git a/deploy/local/launchctl/render.py b/deploy/local/launchctl/render.py
index 1740688b..1f77f3a8 100644
--- a/deploy/local/launchctl/render.py
+++ b/deploy/local/launchctl/render.py
@@ -48,6 +48,7 @@ def replacements() -> dict[str, str]:
"__MYSQL_JDBC_URL__": env("MYSQL_JDBC_URL", identity_jdbc),
"__MYSQL_USERNAME__": env("MYSQL_USERNAME", identity_username),
"__MYSQL_PASSWORD__": env("MYSQL_PASSWORD", identity_password),
+ "__KAFKA_TOPIC_HISTORY_DLQ__": env("KAFKA_TOPIC_HISTORY_DLQ", "vehicle.dlq.history.v1"),
"__YUTONG_MQTT_ENABLED__": env("YUTONG_MQTT_ENABLED", "false"),
"__YUTONG_MQTT_URI__": env("YUTONG_MQTT_URI", ""),
"__YUTONG_MQTT_TOPIC__": env("YUTONG_MQTT_TOPIC", "#"),
diff --git a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/PortainerComposeResourceLimitsTest.java b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/PortainerComposeResourceLimitsTest.java
index 88df5662..14e3f53e 100644
--- a/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/PortainerComposeResourceLimitsTest.java
+++ b/modules/apps/vehicle-history-app/src/test/java/com/lingniu/ingest/historyapp/PortainerComposeResourceLimitsTest.java
@@ -149,6 +149,21 @@ class PortainerComposeResourceLimitsTest {
.contains("keepAlive、连接超时和 TLS PEM 参数");
}
+ @Test
+ void localLaunchctlTemplatesExposeHistoryConsumerDeadLetterTopic() throws IOException {
+ Path launchctl = repositoryRoot().resolve("deploy/local/launchctl");
+ String readme = Files.readString(launchctl.resolve("README.md"));
+ String renderer = Files.readString(launchctl.resolve("render.py"));
+ String historyTemplate = Files.readString(launchctl.resolve("com.lingniu.vehicle-history.plist.template"));
+
+ assertThat(readme)
+ .contains("export KAFKA_TOPIC_HISTORY_DLQ='vehicle.dlq.history.v1'");
+ assertThat(renderer)
+ .contains("\"__KAFKA_TOPIC_HISTORY_DLQ__\": env(\"KAFKA_TOPIC_HISTORY_DLQ\", \"vehicle.dlq.history.v1\")");
+ assertThat(historyTemplate)
+ .contains("KAFKA_TOPIC_HISTORY_DLQ\n __KAFKA_TOPIC_HISTORY_DLQ__");
+ }
+
@Test
void localLaunchctlTemplatesCoverActiveProductionApps() throws IOException {
Path launchctl = repositoryRoot().resolve("deploy/local/launchctl");