Over a million developers have joined DZone.

Microservices Sidecar Pattern Implementation Using Postgres, Spring Cloud Netflix, and Docker

DZone's Guide to

Microservices Sidecar Pattern Implementation Using Postgres, Spring Cloud Netflix, and Docker

Learn how to build a sidecar application and connecting it to a database using the technologies of Postgres, Spring Cloud Netflix, and Docker.

· Microservices Zone ·
Free Resource

Containerized Microservices require new monitoring. See why a new APM approach is needed to even see containerized applications.

Spring Cloud Series

  1. Developing Microservices using Spring Boot, Jersey, Swagger and Docker
  2. Integration Testing using Spring Boot, Postgres and Docker
  3. Services registration and discovery using Spring Cloud Netflix Eureka Server and client-side load-balancing using Ribbon and Feign
  4. Centralized and versioned configuration using Spring Cloud Config Server and Git
  5. Routing requests and dynamically refreshing routes using Spring Cloud Zuul Server
  6. Microservices Sidecar pattern implementation using Postgres, Spring Cloud Netflix and Docker (you are here)
  7. Implementing Circuit Breaker using Hystrix, Dashboard using Spring Cloud Turbine Server (work in progress)

1. Microservices Sidecar Pattern Implementation Using Postgres, Spring Cloud Netflix, and Docker

What's a sidecar? A Sidecar is a companion application of the main service, typically non-JVM, either developed in-house or a 3rd party service (eg Elastic Search, Apache Solr, etc.) where it's desirable for them to take advantage of other infrastructure services such as service registration and discovery, routing, dynamic configuration, monitoring, etc..

This post covers implementing a Sidecar Java application attached to a Postgres database bundled in a Docker image and a Demo client application connecting to the database after retrieving the Postgres metadata (e.g. host, port) from a Eureka registry.

2. Requirements

  • Java 7 or 8.
  • Maven 3.2+
  • Familiarity with Spring Framework.
  • A Eureka server instance for the Spring Cloud Netflix Sidecar application to register the bundled Postgres server with.
  • Docker, local or remote host.

3. The Sidecar Application

can be created like any other Spring Cloud app, from your preferred IDE, http://start.spring.io or from the command line:

curl "https://start.spring.io/starter.tgz"
 -d bootVersion=1.5.9.RELEASE
 -d dependencies=actuator,cloud-eureka
 -d language=java
 -d type=maven-project
 -d baseDir=Sidecar
 -d groupId=com.asimio.cloud
 -d artifactId=sidecar
 -d version=0-SNAPSHOT
 | tar -xzvf -

This command will create a Maven project in a folder named sidecar with most of the dependencies used in the accompanying source code for this post.

The Sidecar relevant files are discussed next:





spring-cloud-starter-eureka includes Eureka client support for applications to register and/or discovery services metadata with/from a remote Eureka server.

spring-cloud-netflix-sidecar provides beans Spring autoconfiguration for a companion application to take care of the service registration and/or discovery of 3rd party services (JVM or not).


package com.asimio.cloud.sidecar;
public class SidecarApplication {

  public static void main(String[] args) {
    SpringApplication.run(SidecarApplication.class, args);

@EnableSidecar annotation might be all needed for this Java application to behave as a companion Sidecar to a 3rd party service (JVM or not) running in the same runtime unit (eg host, VM, Docker container) whose metadata will be registered with a Eureka server.

In the case of non-JVM in-house services, all is needed from them is to provide a Spring Boot health-like endpoint returning something like:


In the case of external services such as Postgres, Elastic Search, Kafka, etc., which likely won't provide a health check as previously described, the Sideacar app itself could be used to implement such requirement:


package com.asimio.cloud.sidecar.healthcheck;
public interface SidecarHealthIndicator extends HealthIndicator {



package com.asimio.cloud.sidecar.config;
public class AppConfig {

  @ConditionalOnProperty(name = "sidecar.postgres.enabled", havingValue = "true", matchIfMissing = false)
  public SidecarHealthIndicator postgresHealthCheck() {
    return new PostgresHealthCheck();

Assuming a Sidecar instance is going to serve as a companion to only one service, it translates to a single SidecarHealthIndicator bean needed per Sidecar application, a PostgresHealthCheck instance in this demo.

Let's back up a little bit, how can it be verified if a Postgres DB is accepting connections? It turns out pg_isready, a Postgres command accomplishes it:

pg_isready -U postgres -h localhost -p 5432
localhost:5432 - rejecting connections

And this is what PostgresHealthCheck.java does:

package com.asimio.cloud.sidecar.healthcheck.postgres;
public class PostgresHealthCheck implements SidecarHealthIndicator {
  // pg_isready U <user> -h localhost -p <sidecarPort>
  private static final String COMMAND_PATTERN = "pg_isready -U %s -h localhost -p %s";

  private int sidecarPort;

  public Health health() {
    Health.Builder result = null;
    try {
      String output = this.runCommand();
      if (output.indexOf("accepting connections") != -1) {
        result = Health.up();
      } else if (output.indexOf("rejecting connections") != -1 || output.indexOf("no response") != -1) {
        result = Health.down().withDetail("reason", output);
    } catch (IOException e) {
      LOGGER.warn("Failed to execute command.", e);
      result = Health.down().withException(e);
    return result.build();

The health() method will return:




depending on the output of the OS command.

The last piece of code needed in this case, where the service desired to register with a Eureka server is outside of our control, is to expose its Health information via an endpoint the Eureka server can send requests to.


package com.asimio.cloud.sidecar.web;
public class LocalStatusDelegatorController {

  private SidecarHealthIndicator healthIndicator;

  public Health sidecarHealthStatus() {
    return this.healthIndicator.health();

The Sidecar application exposes the endpoint /delegating-status which uses a health check to run a specific OS command to verify if the 3rd party service is usable.

Let's look at the Sidecar configuration files:

# Generic app name, should be passed from command line (eg java -jar ... --sidecar.appName=POSTGRES-DB_DVDRENTAL ...)
    name: ${sidecar.appName:Generic Sidecar}

The sidecar.appName property value is what's going to be used for registration and discovery purposes.


  hostname: localhost
  # port should be passed from command line (eg java -jar ... --sidecar.port=5432 ...)
  port: 5432
  # health-uri the service uri returning health data in the form of { "status": "UP" } or
  # http://localhost:${sidecar.port}/${health-uri:health.json} if the service provides such endpoint.
  health-uri: http://localhost:${server.port}/delegating-status
  # Sidecar controller
  home-page-uri: http://${sidecar.hostname}:${server.port}/
    enabled: true
    registerWithEureka: true
    fetchRegistry: false
      defaultZone: http://localhost:8000/eureka/
    appname: ${spring.application.name}
    hostname: ${sidecar.hostname}
    statusPageUrlPath: ${management.context-path}/info
    healthCheckUrlPath: ${sidecar.health-uri}
    preferIpAddress: true
      instanceId: ${sidecar.appName}:${sidecar.port}

Notice the sidecar.hostname property (used in Eureka client configuration) is hardcoded to localhost because the Sidecar companion application is supposed to run in the same host / VM / Docker container / ... as the 3rd party service intended to be discovered.

Other relevant settings are the sidecar.port property set to the port the 3rd party service listens on. The sidecar.health-uri property pointing to the /delegating-status endpoint, used by the Eureka server to get information about the availability of the service. And sidecar.postgres.enabled, which causes the application to act as a Sidecar for Postgres.

It still needs to be built so that the resulting artifact could be used in a Docker image along with Postgres.

mvn clean package

which should create the application target/sidecar.jar.

4. Set Up Postgres DVD Rental Database and Sidecar Application in a Docker Image

asimio/db_dvdrental Docker image, used in Integration Testing using Spring Boot, Postgres and Docker would be the starting point to bundle a Postgres DB with the Sidecar companion application.

cp target/sidecar.jar /<path to>/postgres/db_dvdrental-sidecar/scripts/


FROM asimio/db_dvdrental:latest
MAINTAINER Orlando L Otero ootero@asimio.net, https://bitbucket.org/asimio/postgres
# Manually build using command: docker build -t asimio/db_dvdrental-sidecar:latest .

# Install JDK
  mkdir -p /usr/lib && \
  wget --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u162-b12/0da788060d494f5095bf8624735fa2f1/jdk-8u162-linux-x64.tar.gz  && \
  tar -zxf jdk-8u162-linux-x64.tar.gz -C /usr/lib && \
  ln -s /usr/lib/jdk1.8.0_162 /usr/lib/jdk && \
  chown -R root:root /usr/lib/jdk && \
  rm jdk-8u162-linux-x64.tar.gz  

ENV JAVA_HOME="/usr/lib/jdk"
ENV JAVA_TOOL_OPTIONS="-Xms256M -Xmx256M -Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom"

COPY scripts/sidecar.jar /opt/asimio-cloud/sidecar.jar
ADD scripts/sidecar.sh /docker-entrypoint-initdb.d/
RUN chmod 755 /docker-entrypoint-initdb.d/sidecar.sh

This Dockerfile bundles a Postgres DB, the Sidecar application and a shell file to start the Sidecar app in the background when a Docker container is started.



echo "Starting sidecar application"
java -jar /opt/asimio-cloud/sidecar.jar &

The image can be built running:

cd /<path to>/postgres/db_dvdrental-sidecar
docker build -t asimio/db_dvdrental-sidecar:latest .
Successfully built 3955eae0bf98
Successfully tagged asimio/db_dvdrental-sidecar:latest

Note: Running a Docker container using this image causes the container to start two processes, which is not a suggested practice when using Docker. An alternative would be to start a Docker container for the Sidecar application and another container for the main application, this setup requires the Sidecar to know the host/IP of the main application, as well as some code changes for the Sidecar to report the correct hostname to the Eureka registry. Still, I decided to run the two processes in the same container in this tutorial since this would be a very close approach to take when running them on bare metal or VMs.

5. Starting the Eureka Server

This demo was run using Docker where communication between containers is needed, so I'll first create a Docker network for them to run on:

docker network create -d bridge --subnet sidecarpostgresdemo_default

docker run -idt -p 8000:8000 --network=sidecarpostgresdemo_default -e spring.profiles.active=standalone -e server.port=8000 -e hostName=$HOSTNAME asimio/discovery-server:1.0.73

docker logs 87
2018-02-13 04:10:21.156  INFO 1 --- [      Thread-10] e.s.EurekaServerInitializerConfiguration : Started Eureka Server
2018-02-13 04:10:21.201  INFO 1 --- [           main] b.c.e.u.UndertowEmbeddedServletContainer : Undertow started on port(s) 8000 (http)
2018-02-13 04:10:21.202  INFO 1 --- [           main] c.n.e.EurekaDiscoveryClientConfiguration : Updating port to 8000
2018-02-13 04:10:21.207  INFO 1 --- [           main] c.a.c.eureka.EurekaServerApplication     : Started EurekaServerApplication in 6.493 seconds (JVM running for 7.121)

curl http://localhost:8000/eureka/apps

Logs and a request to Eureka server confirm it started successfully and no application has been registered yet.

6. Starting the Postgres DVD Rental Database and Registering It With Eureka via the Sidecar App

Similarly to starting the Eureka server, a container including Postgres and Sidecar apps is started specifying the same network for containers to reach each other:

docker run -d -p 5432:5432 -p 8080:8080 --network=sidecarpostgresdemo_default -e DB_NAME=db_dvdrental -e DB_USER=user_dvdrental -e DB_PASSWD=changeit -e sidecar.port=5432 -e sidecar.appName=POSTGRES-DB_DVDRENTAL -e eureka.client.serviceUrl.defaultZone= asimio/db_dvdrental-sidecar:latest

docker logs 25
/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/db-init.sh
Verifying DB db_dvdrental presence ...
db_dvdrental DB does not exist, creating it ...
Verifying role user_dvdrental presence ...
user_dvdrental role does not exist, creating it ...
user_dvdrental role successfully created
db_dvdrental DB successfully created

/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/db-restore.sh
Importing data into DB db_dvdrental
db_dvdrental DB restored from backup
Granting permissions in DB 'db_dvdrental' to role 'user_dvdrental'.
Permissions granted

/usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/sidecar.sh
Starting sidecar application: FIXME {sidecar.appName}
2018-02-13 04:40:06.683  INFO 136 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_POSTGRES-DB_DVDRENTAL/252c8fc4703d:POSTGRES-DB_DVDRENTAL:8080: registering service...
2018-02-13 04:40:06.858  INFO 136 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-02-13 04:40:06.861  INFO 136 --- [           main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8080
2018-02-13 04:40:06.867  INFO 136 --- [           main] c.a.cloud.sidecar.SidecarApplication     : Started SidecarApplication in 7.395 seconds (JVM running for 7.924)
2018-02-13 04:40:06.982  INFO 136 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_POSTGRES-DB_DVDRENTAL/252c8fc4703d:POSTGRES-DB_DVDRENTAL:8080 - registration status: 204
2018-02-13 04:40:07.087  INFO 136 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2018-02-13 04:40:07.087  INFO 136 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2018-02-13 04:40:07.108  INFO 136 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 20 ms
2018-02-13 04:40:07.126  WARN 136 --- [nio-8080-exec-2] o.s.c.n.zuul.web.ZuulHandlerMapping      : No routes found from RouteLocator
2018-02-13 04:40:07.204  INFO 136 --- [nio-8080-exec-2] c.a.c.s.h.postgres.PostgresHealthCheck   : localhost:5432 - accepting connections

See how a Postgres DB is setup first, then the Sidecar companion app successfully started and ... PostgresHealthCheck : localhost:5432 - accepting connections log indicates the Sidecar is able to connect to the Postgres server.

Sending a request to the Eureka server now results in a POSTGRES-DB_DVDRENTAL service metadata stored in the registry, with [host=, port= 5432].

curl http://localhost:8000/eureka/apps
      <port enabled="true">5432</port>
      <securePort enabled="false">443</securePort>
      <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">

7. The Postgres Client Demo Application

Setting up a Demo client application could be done following a similar approach as when creating the sidecar application.



These are the main dependencies needed for the Demo Postgres client application to locate and connect to a Postgres DB.


package com.asimio.demo;
public class SidecarPostgresDemoApplication {

  public static void main(String[] args) {
    SpringApplication.run(SidecarPostgresDemoApplication.class, args);

@EnableEurekaClient annotation along with configuration properties allow this application to register and/or discover metadata from a Eureka server.


package com.asimio.demo.config;
@EnableConfigurationProperties({ DataSourceProperties.class })
public class AppConfig {
  private DataSourceProperties dsProperties;

  private DiscoveryClient discoveryClient;

  private String dbServiceName;

  public DataSource dataSource() {
    ServiceInstance instance = this.discoveryClient.getInstances(this.dbServiceName).iterator().next();
    return this.createDataSource(instance.getHost(), instance.getPort());

  private DataSource createDataSource(String host, int port) {
    String jdbcUrl = String.format(this.dsProperties.getUrl(), host, port);
    DataSourceBuilder factory = DataSourceBuilder
    return factory.build();

A DataSource bean is instantiated instead of relying in DataSourceAutoConfiguration because the hostname and port the Postgres server listens on is unknown until Eureka responds back with metadata.


      - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
    name: db_dvdrental
    # Placeholder to be replaced at runtime with metadata retrieved from Registration server
    url: jdbc:postgresql://%s:%s/db_dvdrental
    username: user_dvdrental
    password: changeit
    driverClassName: org.postgresql.Driver

    database: POSTGRESQL
    database-platform: org.hibernate.dialect.PostgreSQLDialect
    generate-ddl: false
      ddl-auto: none

    registerWithEureka: false
    fetchRegistry: true
      defaultZone: http://localhost:8000/eureka/

Notice the JDBC URL includes placeholders to be replaced with values coming from Eureka.

mvn clean package -DskipTests
mvn docker:build

I'll skip running the integration tests for now because this demo application needs a Eureka and Postgres servers to connect to while loading the Spring context. In Integration Testing using Spring Boot, Postgres and Docker I covered how to start dependent services before running each test.

8. Starting the Postgres Client Demo

Running this demo in a Docker container requires the usage of the same network the other two containers use:

docker run -idt -p 8090:8090 --network=sidecarpostgresdemo_default -e server.port=8090 -e eureka.client.serviceUrl.defaultZone= asimio/sidecar-postgres-demo:latest

docker logs 67
2018-02-13 05:03:48.248  INFO 1 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8090 (http)
2018-02-13 05:03:48.249  INFO 1 --- [           main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8090
2018-02-13 05:03:48.255  INFO 1 --- [           main] c.a.demo.SidecarPostgresDemoApplication  : Started SidecarPostgresDemoApplication in 9.586 seconds (JVM running for 10.059)

Now that it has started, let's send a couple of requests to an endpoint which should successfully execute a DB query:

curl http://localhost:8090/actors/1
[actor: Penelope Guiness]

curl http://localhost:8090/actors/2
[actor: Nick Wahlberg]

A relevant note before finishing showing how to implement the Sidecar pattern using Postgres, Spring Cloud Netflix, and Docker.

You probably noticed I have started the Eureka server Docker container first, then a Postgres container bundled with a Sidecar Java application and lastly the Demo Restful service container which connects to a Postgres DB once it finds its metadata from Eureka. I actually included a Docker compose file in an attempt to simplify and automate this process but not without its challenges.

Connection-related problems arose when starting the Demo API application but the Eureka server hasn't started yet or when the Postgres host metadata is not still available in the Eureka server while instantiating the Datasource bean.

I believe it would be a good practice for microservices to recover themselves, to self-heal from a situation like this, where services startup order is ideal but not required, where an application would keep trying to connect to dependent services for a given time and/or a number of attempts. Would this be a concern of the application or a concern of some kind of platform orchestrator? Stay tuned, I might follow up with an implementation of this approach in another post.

9. Source Code

Accompanying source code for this blog post can be found at:

10. References

Automatically manage containers and microservices with better control and performance using Instana APM. Try it for yourself today.

docker ,spring boot ,eureka ,spring cloud ,netflixoss ,sidecar ,java ,microservices ,postgres ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}