Consul Integration
HashiCorp Consul is a service mesh solution that provides service discovery, health checking, and key-value storage. This guide shows how to integrate Spring Boot Admin with Consul.
Overview
With Consul integration:
- Applications register with Consul
- Spring Boot Admin Server discovers applications via Consul
- Built-in health checks
- No Spring Boot Admin Client library required
Architecture
Setting Up Consul
Install Consul
# macOS
brew install consul
# Linux
wget https://releases.hashicorp.com/consul/1.17.0/consul_1.17.0_linux_amd64.zip
unzip consul_1.17.0_linux_amd64.zip
sudo mv consul /usr/local/bin/
# Docker
docker run -d --name=consul -p 8500:8500 consul:latest
Start Consul
# Development mode
consul agent -dev
# Production mode
consul agent -server -bootstrap-expect=1 -data-dir=/tmp/consul
Access Consul UI at: http://localhost:8500
Configuring Spring Boot Admin Server
Add Dependencies
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
</dependencies>
Enable Discovery
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@EnableAdminServer
@SpringBootApplication
public class SpringBootAdminConsulApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootAdminConsulApplication.class, args);
}
}
Configure Consul Client
spring:
application:
name: spring-boot-admin-server
cloud:
consul:
host: localhost
port: 8500
discovery:
preferIpAddress: true
health-check-interval: 10s
health-check-path: /actuator/health
instance-id: ${spring.application.name}:${random.value}
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS
Ignore Consul Service
Don't monitor Consul itself:
spring:
boot:
admin:
discovery:
ignored-services: consul
Configuring Client Applications
Add Dependencies
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
Enable Discovery
@EnableDiscoveryClient
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Configure Application
spring:
application:
name: my-application
cloud:
consul:
host: localhost
port: 8500
discovery:
metadata:
management-context-path: ${management.server.base-path:/actuator}
health-path: ${management.endpoints.web.path-mapping.health:health}
management:
endpoints:
web:
exposure:
include: "*"
base-path: /actuator
endpoint:
health:
show-details: ALWAYS
Metadata in Consul
Important: No Dots in Keys
Consul does not allow dots (.) in metadata keys. Use dashes (-) or underscores (_) instead.
Wrong:
metadata:
user.name: admin # ❌ Won't work
user.password: secret # ❌ Won't work
Correct:
metadata:
user-name: admin # ✅ Works
user-password: secret # ✅ Works
Adding Custom Metadata
spring:
cloud:
consul:
discovery:
metadata:
management-context-path: /actuator
health-path: /ping
user-name: ${spring.security.user.name}
user-password: ${spring.security.user.password}
tags-environment: production
tags-region: us-east-1
team: platform
Tags
Consul supports tags for grouping:
spring:
cloud:
consul:
discovery:
tags:
- production
- us-east-1
- platform-team
Custom Management Configuration
Different Management Port
server:
port: 8080
management:
server:
port: 9090
endpoints:
web:
base-path: /management
spring:
cloud:
consul:
discovery:
metadata:
management-port: 9090
management-context-path: /management
Custom Health Check Path
management:
endpoints:
web:
path-mapping:
health: /ping
base-path: /actuator
spring:
cloud:
consul:
discovery:
health-check-path: /actuator/ping
metadata:
health-path: /ping
Health Checks
Default Health Check
Consul automatically creates HTTP health check:
spring:
cloud:
consul:
discovery:
health-check-interval: 10s
health-check-timeout: 5s
health-check-path: /actuator/health
Custom Health Check
spring:
cloud:
consul:
discovery:
health-check-url: https://my-app.example.com/actuator/health
health-check-interval: 15s
health-check-critical-timeout: 30s
TTL Health Check
Use TTL-based health check instead of HTTP:
spring:
cloud:
consul:
discovery:
health-check-interval: 10s
heartbeat:
enabled: true
ttl-value: 15
ttl-unit: s
Service Filtering
By Service Name
spring:
boot:
admin:
discovery:
ignored-services: consul,config-server
By Metadata
@Bean
public InstanceFilter consulInstanceFilter() {
return registration -> {
// Only monitor services with 'monitor' tag
Map<String, String> metadata = registration.getMetadata();
return "true".equals(metadata.get("monitor"));
};
}
By Tags
@Bean
public InstanceFilter tagBasedFilter() {
return registration -> {
String tags = registration.getMetadata().get("tags");
return tags != null && tags.contains("production");
};
}
Securing Consul
ACL Token
spring:
cloud:
consul:
host: localhost
port: 8500
discovery:
acl-token: ${CONSUL_ACL_TOKEN}
TLS/SSL
spring:
cloud:
consul:
host: localhost
port: 8501
scheme: https
tls:
enabled: true
cert-path: /path/to/cert.pem
key-path: /path/to/key.pem
ca-cert-path: /path/to/ca.pem
Docker Compose Example
version: '3'
services:
consul:
image: consul:latest
ports:
- "8500:8500"
- "8600:8600/udp"
command: agent -server -ui -bootstrap-expect=1 -client=0.0.0.0
spring-boot-admin:
build: ./admin-server
ports:
- "8080:8080"
environment:
- SPRING_CLOUD_CONSUL_HOST=consul
depends_on:
- consul
my-application:
build: ./my-app
ports:
- "8081:8081"
environment:
- SPRING_CLOUD_CONSUL_HOST=consul
depends_on:
- consul
Kubernetes Integration
For Kubernetes, use Consul Connect:
spring:
cloud:
consul:
host: consul.service.consul
port: 8500
discovery:
preferIpAddress: false
hostname: ${HOSTNAME}.my-app.default.svc.cluster.local
metadata:
k8s-namespace: ${POD_NAMESPACE:default}
k8s-pod: ${HOSTNAME}
Troubleshooting
Service Not Appearing
-
Check Consul registration:
curl http://localhost:8500/v1/catalog/services
curl http://localhost:8500/v1/health/service/my-application -
Verify health check:
consul catalog services
consul catalog nodes -service=my-application -
Check metadata:
curl http://localhost:8500/v1/catalog/service/my-application | jq
Metadata Not Working
Ensure no dots in keys:
# Wrong
metadata:
user.name: admin
# Correct
metadata:
user-name: admin
Health Check Failing
Verify endpoint is accessible:
curl http://localhost:8081/actuator/health
Check Consul health status:
consul catalog nodes -service=my-application -detailed
Deregistration Issues
Configure critical timeout:
spring:
cloud:
consul:
discovery:
health-check-critical-timeout: 30s
Best Practices
-
Use Instance IDs: Ensure unique instance identifiers
spring:
cloud:
consul:
discovery:
instance-id: ${spring.application.name}:${random.value} -
Configure Health Checks: Set appropriate intervals
spring:
cloud:
consul:
discovery:
health-check-interval: 10s
health-check-critical-timeout: 1m -
Use Metadata for Credentials: Avoid hardcoding
spring:
cloud:
consul:
discovery:
metadata:
user-name: ${ACTUATOR_USER}
user-password: ${ACTUATOR_PASSWORD} -
Prefer IP Address: For container environments
spring:
cloud:
consul:
discovery:
preferIpAddress: true -
Use Tags: For service categorization
spring:
cloud:
consul:
discovery:
tags:
- production
- microservice -
Enable Deregistration: Clean up on shutdown
spring:
cloud:
consul:
discovery:
deregister: true -
Monitor Consul Health: Ensure Consul is operational
consul members
consul info
Complete Example
See the spring-boot-admin-sample-consul project for a complete working example.
See Also
- Service Discovery - Service discovery overview
- Consul Sample - Detailed sample walkthrough
- Metadata - Working with metadata
- Security - Securing discovered services