Consul Sample
The Consul sample demonstrates Spring Boot Admin Server integration with HashiCorp Consul for service discovery. This sample shows how to leverage Consul's powerful service registry and health checking capabilities to automatically discover and monitor Spring Boot applications.
Overview
Location: spring-boot-admin-samples/spring-boot-admin-sample-consul/
Features:
- Automatic service discovery via Consul
- No Admin Client required on monitored applications
- Consul health check integration
- Metadata-based configuration
- Custom actuator endpoint paths
- Spring Security integration
- Servlet-based deployment
Prerequisites
- Java 17 or higher
- Maven 3.6+
- Consul installed and running
Installing 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/
Windows
Download from: https://www.consul.io/downloads
Docker
docker run -d -p 8500:8500 -p 8600:8600/udp --name=consul consul agent -server -ui -bootstrap-expect=1 -client=0.0.0.0
Verify Installation
consul version
Running the Sample
Start Consul
# Development mode (single node)
consul agent -dev
Verify Consul is running: http://localhost:8500/ui
Start Admin Server
cd spring-boot-admin-samples/spring-boot-admin-sample-consul
mvn spring-boot:run
Access Admin UI at: http://localhost:8080
With Different Consul Host
mvn spring-boot:run -Dspring-boot.run.arguments=\
--spring.cloud.consul.host=consul-server
Insecure Mode
mvn spring-boot:run -Dspring-boot.run.profiles=insecure
Project Structure
Dependencies
<dependencies>
<!-- Admin Server -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<!-- Consul Discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- Web (Servlet) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<!-- Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
Main Application Class
@SpringBootApplication
@EnableDiscoveryClient // Enable Consul discovery
@EnableAdminServer // Enable Admin Server
public class SpringBootAdminConsulApplication {
static void main(String[] args) {
SpringApplication.run(SpringBootAdminConsulApplication.class, args);
}
}
Configuration
Admin Server Configuration
spring:
application:
name: consul-example
cloud:
config:
enabled: false # Disable config client
consul:
host: localhost
port: 8500
discovery:
metadata:
# IMPORTANT: Use dashes, not dots in metadata keys!
management-context-path: /foo
health-path: /ping
user-name: user
user-password: password
profiles:
active:
- secure
boot:
admin:
discovery:
ignored-services: consul # Don't monitor Consul itself
management:
endpoints:
web:
exposure:
include: "*"
base-path: /foo # Custom actuator base path
path-mapping:
health: /ping # Custom health endpoint path
endpoint:
health:
show-details: ALWAYS
CRITICAL: Consul metadata keys cannot contain dots. Use dashes instead:
- ✅
management-context-path - ❌
management.context-path
This is a Consul limitation, not a Spring Boot Admin limitation.
Client Application Configuration
For applications to be monitored:
spring:
application:
name: my-service
cloud:
consul:
host: localhost
port: 8500
discovery:
metadata:
management-context-path: /actuator # Use dashes!
health-path: /actuator/health
# For secured actuators
user-name: ${actuator.username}
user-password: ${actuator.password}
management:
endpoints:
web:
exposure:
include: "*"
Security Configuration
Insecure Profile
@Profile("insecure")
@Configuration
public static class SecurityPermitAllConfig {
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http)
throws Exception {
http.authorizeHttpRequests((authorizeRequests) ->
authorizeRequests.anyRequest().permitAll())
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(
adminContextPath + "/instances",
adminContextPath + "/instances/*",
adminContextPath + "/actuator/**"
));
return http.build();
}
}
Secure Profile (Default)
@Profile("secure")
@Configuration
public static class SecuritySecureConfig {
@Bean
protected SecurityFilterChain filterChain(HttpSecurity http)
throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler =
new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(adminContextPath + "/");
http.authorizeHttpRequests((authorizeRequests) ->
authorizeRequests
.requestMatchers(adminContextPath + "/assets/**")
.permitAll()
.requestMatchers(adminContextPath + "/login")
.permitAll()
.anyRequest()
.authenticated())
.formLogin((formLogin) -> formLogin
.loginPage(adminContextPath + "/login")
.successHandler(successHandler))
.logout((logout) -> logout
.logoutUrl(adminContextPath + "/logout"))
.httpBasic(Customizer.withDefaults())
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers(
adminContextPath + "/instances",
adminContextPath + "/instances/*",
adminContextPath + "/actuator/**"
));
return http.build();
}
}
How It Works
Service Discovery Flow
-
Application Registration:
- Application registers with Consul on startup
- Sends service metadata including actuator paths
- Consul assigns service ID and health checks
-
Health Checking:
- Consul performs HTTP health checks
- Marks services as passing/failing
- Admin Server queries only healthy services
-
Admin Discovery:
- Admin Server queries Consul for registered services
- Reads metadata to locate actuator endpoints
- Begins monitoring discovered services
-
Deregistration:
- Application deregisters on shutdown
- Consul removes from registry
- Admin Server stops monitoring
Metadata Mapping
Admin Server reads specific metadata keys from Consul:
spring:
cloud:
consul:
discovery:
metadata:
# Required for endpoint detection
management-context-path: /actuator # Dashes only!
management-port: 8081 # If different
# Optional - for secured actuators
user-name: admin
user-password: ${ACTUATOR_PASSWORD}
# Custom metadata
environment: production
version: 1.0.0
team: platform
Key Mappings:
management-context-path→ Where to find actuator endpointsmanagement-port→ Management port if different from service porthealth-path→ Custom health endpoint pathuser-name/user-password→ Actuator credentials
Custom Actuator Paths
This sample demonstrates custom actuator paths:
management:
endpoints:
web:
base-path: /foo # Actuator at /foo instead of /actuator
path-mapping:
health: /ping # Health at /foo/ping instead of /foo/health
Admin Server discovers these via metadata:
spring:
cloud:
consul:
discovery:
metadata:
management-context-path: /foo
health-path: /ping
Testing the Sample
Verify Consul Registration
- Access Consul UI:
http://localhost:8500/ui - Navigate to "Services"
- Should see:
consul-example(Admin Server)- Other registered services
Check Service Health
In Consul UI, services should show:
- Status: Passing (green)
- Health check URL displayed
- Metadata visible
Verify Admin Discovery
- Access Admin UI:
http://localhost:8080 - Should see services registered in Consul
- Click service to view:
- Health status
- Metrics
- Environment
- Custom /ping endpoint
Test Dynamic Discovery
Register a new service:
# Register via Consul API
curl -X PUT -d '{
"Name": "test-service",
"Address": "127.0.0.1",
"Port": 8081,
"Meta": {
"management-context-path": "/actuator"
},
"Check": {
"HTTP": "http://127.0.0.1:8081/actuator/health",
"Interval": "10s"
}
}' http://localhost:8500/v1/agent/service/register
Service appears in Admin UI within seconds.
Consul Features
Health Checks
Consul supports multiple health check types:
HTTP Health Check
spring:
cloud:
consul:
discovery:
health-check-path: /actuator/health
health-check-interval: 10s
health-check-timeout: 5s
TTL Health Check
spring:
cloud:
consul:
discovery:
health-check-ttl: 30s
Application must send heartbeat to Consul every 30 seconds.
Service Tags
Add tags for filtering:
spring:
cloud:
consul:
discovery:
tags:
- production
- backend
- v1.0.0
Service Filtering
Filter services monitored by Admin:
spring:
boot:
admin:
discovery:
ignored-services:
- consul # Don't monitor Consul
- config-server # Don't monitor Config Server
services: # Only monitor these (if specified)
- my-service
- another-service
Advanced Configuration
Consul ACL
Secure Consul with ACL tokens:
spring:
cloud:
consul:
token: ${CONSUL_TOKEN}
discovery:
acl-token: ${CONSUL_ACL_TOKEN}
Consul TLS
Connect to Consul over TLS:
spring:
cloud:
consul:
scheme: https
tls:
enabled: true
key-store-path: classpath:consul-keystore.p12
key-store-password: ${KEYSTORE_PASSWORD}
Multiple Datacenters
Register in specific datacenter:
spring:
cloud:
consul:
discovery:
datacenter: dc1
Prefer IP Address
Use IP instead of hostname:
spring:
cloud:
consul:
discovery:
prefer-ip-address: true
ip-address: 192.168.1.100
Comparison: Consul vs. Eureka
| Feature | Consul | Eureka |
|---|---|---|
| Health Checks | Built-in (HTTP, TCP, TTL, Script) | Via Spring Boot actuator only |
| Key-Value Store | Yes | No |
| ACL | Yes | Basic |
| Multi-DC | Native support | Requires setup |
| DNS Interface | Yes | No |
| Metadata Keys | No dots allowed | Dots allowed |
| Complexity | Higher | Lower |
| Ecosystem | HashiCorp ecosystem | Netflix stack |
Troubleshooting
Metadata Key Errors
Symptom: Admin Server can't find actuator endpoints
Cause: Used dots in metadata keys
Solution: Use dashes instead:
# Wrong
metadata:
management.context-path: /actuator
# Correct
metadata:
management-context-path: /actuator
Services Not Discovered
Check Consul connectivity:
# Test Consul API
curl http://localhost:8500/v1/catalog/services
# Check health
curl http://localhost:8500/v1/health/state/passing
Verify Admin logs:
tail -f logs/spring-boot-admin.log | grep -i consul
Health Check Failures
Services show as "failing" in Consul:
-
Verify health endpoint is accessible:
curl http://localhost:8080/actuator/health -
Check health check interval:
spring:
cloud:
consul:
discovery:
health-check-interval: 30s # Increase if needed -
Review Consul logs:
consul monitor
Connection Timeouts
Increase timeout values:
spring:
cloud:
consul:
discovery:
health-check-timeout: 10s # Increase from default
Production Considerations
Consul Cluster
Run Consul in cluster mode (3 or 5 nodes):
# Server node 1
consul agent -server -bootstrap-expect=3 -data-dir=/consul/data \
-bind=192.168.1.10
# Server node 2
consul agent -server -data-dir=/consul/data \
-bind=192.168.1.11 -join=192.168.1.10
# Server node 3
consul agent -server -data-dir=/consul/data \
-bind=192.168.1.12 -join=192.168.1.10
Enable ACL
spring:
cloud:
consul:
token: ${CONSUL_MANAGEMENT_TOKEN}
discovery:
acl-token: ${CONSUL_SERVICE_TOKEN}
Monitor Consul Health
Register Admin Server to monitor itself:
spring:
boot:
admin:
discovery:
ignored-services: [] # Don't ignore any services
Key Takeaways
This sample demonstrates:
✅ Consul Integration
- Service discovery via Consul
- Health check integration
✅ Metadata Handling
- Proper metadata key formatting (dashes not dots)
- Custom actuator paths
✅ Production Features
- ACL support
- TLS encryption
- Multi-datacenter awareness
✅ Flexibility
- Custom endpoint paths
- Secure and insecure modes
Next Steps
- Explore Eureka Sample for Netflix Eureka
- Review Zookeeper Sample for Apache Zookeeper
- Check Consul Integration Guide for detailed setup
- See Hazelcast Sample for clustering