<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Paolo fernando Flores Rivera</title>
    <description>The latest articles on DEV Community by Paolo fernando Flores Rivera (@paolo_fernandofloresriv).</description>
    <link>https://dev.to/paolo_fernandofloresriv</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3468554%2F37a8fd6c-a20c-4e02-866d-59dc97202b9e.jpg</url>
      <title>DEV Community: Paolo fernando Flores Rivera</title>
      <link>https://dev.to/paolo_fernandofloresriv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/paolo_fernandofloresriv"/>
    <language>en</language>
    <item>
      <title>Implementing the Third Principle of the 12-Factor App in Spring Boot and Kubernetes</title>
      <dc:creator>Paolo fernando Flores Rivera</dc:creator>
      <pubDate>Tue, 03 Mar 2026 23:46:17 +0000</pubDate>
      <link>https://dev.to/paolo_fernandofloresriv/12-factor-app-principles-with-springboot-apps-and-kubernetes-hh4</link>
      <guid>https://dev.to/paolo_fernandofloresriv/12-factor-app-principles-with-springboot-apps-and-kubernetes-hh4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we will implement the third principle of the 12-Factor Application methodology in a Spring Boot and Kubernetes environment. Doing so allows us to avoid common problems such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Applications running with incorrect configuration for a specific environment&lt;/li&gt;
&lt;li&gt;Delegating the responsibility for correct configuration to the DevOps role rather than developers&lt;/li&gt;
&lt;li&gt;Minimizing risk related to misconfiguration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When working across multiple environments with microservices, a common anti-pattern causes frequent issues — particularly in production. This pattern consists of assuming that everything has been compiled correctly with the right configuration. Instead, this tutorial demonstrates how to properly implement the third principle of the 12-Factor App methodology.&lt;/p&gt;

&lt;p&gt;As stated in the &lt;a href="https://12factor.net/config" rel="noopener noreferrer"&gt;12-Factor App&lt;/a&gt; documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy to change between deploys without changing any code; unlike config files, there is little chance of them being checked into the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A running Kubernetes cluster. If you do not have one, you can run a local cluster using tools such as &lt;a href="https://kind.sigs.k8s.io/" rel="noopener noreferrer"&gt;kind&lt;/a&gt; or &lt;a href="https://minikube.sigs.k8s.io/" rel="noopener noreferrer"&gt;minikube&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;One or more Spring Boot applications deployed to Kubernetes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Consider a business with two applications: a &lt;strong&gt;users&lt;/strong&gt; service and a &lt;strong&gt;transactions&lt;/strong&gt; service. Suppose there is a specific configuration for a development (&lt;code&gt;dev&lt;/code&gt;) environment and sensitive configuration for a production (&lt;code&gt;prod&lt;/code&gt;) environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Workaround
&lt;/h3&gt;

&lt;p&gt;A typical workaround is to define multiple &lt;code&gt;application.properties&lt;/code&gt; profiles in Spring Boot, one per environment, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── application.properties
├── dev-application.properties
└── prod-application.properties
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A variable is then defined in &lt;code&gt;application.properties&lt;/code&gt; and its value is changed manually each time a build is required for a specific environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;spring.profiles.active&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pitfalls of this approach:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This solution requires a separate compilation per environment. If, for example, there are four environments (DEV, QA, TEST, and PROD) and five microservices, the team must perform twenty compilations instead of five. More critically, this approach is error-prone: a service could be compiled for the &lt;code&gt;dev&lt;/code&gt; environment and accidentally deployed to production, resulting in poor customer experience or financial loss.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practice
&lt;/h2&gt;

&lt;p&gt;The recommended approach is to store environment-specific configuration within the environment itself and have the application consume it at runtime. While Spring Boot provides a Config Server, this introduces additional infrastructure costs — particularly in cloud architectures where CPU and memory resources are at a premium. For Kubernetes-native microservices, there is a simpler and more cost-effective solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overview of the solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define an &lt;code&gt;application.properties&lt;/code&gt; file with the values for the target environment (e.g., &lt;code&gt;dev&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Transform this file into a Kubernetes &lt;code&gt;ConfigMap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Modify the Deployment manifest to mount the &lt;code&gt;application.properties&lt;/code&gt; file at the path &lt;code&gt;/config/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Configure Spring Boot to load its configuration from this mounted path instead of the compiled artifact.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Detailed Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Define the ConfigMap
&lt;/h3&gt;

&lt;p&gt;Create a properties file with the desired configuration for your environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;spring.profiles.active&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;
&lt;span class="py"&gt;server.port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8083&lt;/span&gt;

&lt;span class="c"&gt;# JPA configuration
&lt;/span&gt;&lt;span class="py"&gt;spring.jpa.database&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;POSTGRESQL&lt;/span&gt;
&lt;span class="py"&gt;spring.jpa.hibernate.ddl-auto&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;
&lt;span class="py"&gt;spring.jpa.properties.hibernate.default_schema&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;public&lt;/span&gt;
&lt;span class="py"&gt;spring.jpa.generate-ddl&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;

&lt;span class="c"&gt;# SQL initialization configuration
&lt;/span&gt;&lt;span class="py"&gt;spring.sql.init.platform&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a Kubernetes &lt;code&gt;ConfigMap&lt;/code&gt; from the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create configmap application-properties &lt;span class="nt"&gt;--from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;application.properties
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Configure the Deployment Manifest
&lt;/h3&gt;

&lt;p&gt;According to the &lt;a href="https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html" rel="noopener noreferrer"&gt;official Spring Boot documentation&lt;/a&gt;, it is possible to define an external configuration location using the &lt;code&gt;SPRING_CONFIG_LOCATION&lt;/code&gt; environment variable. When this variable is set, Spring Boot will ignore the compiled artifact &lt;code&gt;application.properties&lt;/code&gt; and instead load configuration from the specified path.&lt;/p&gt;

&lt;p&gt;Additionally, the &lt;code&gt;application.properties&lt;/code&gt; file from the &lt;code&gt;ConfigMap&lt;/code&gt; must be mounted into the container using a Kubernetes volume. The following Deployment manifest illustrates this configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-transactions-app&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-transactions-app&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-transactions-app&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-transactions-app&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;examplebusiness/api-transactions:latest&lt;/span&gt;
        &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
          &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_CONFIG_LOCATION&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;file:/app/conf/transactions-additional.properties&lt;/span&gt;
        &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app/conf/&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application-properties&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application-properties&lt;/span&gt;
        &lt;span class="na"&gt;configMap&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application-properties&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this configuration in place, every time the application starts — whether in development or production — it will load the environment-specific configuration that was injected at the infrastructure level.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pros and Cons
&lt;/h2&gt;

&lt;p&gt;This approach migrates the configuration pipeline from an error-prone process to a safer, more reliable workflow. However, there are trade-offs to consider.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single compiled artifact is used across all environments, eliminating redundant builds.&lt;/li&gt;
&lt;li&gt;Configuration is decoupled from the application code, reducing the risk of environment-specific values being committed to the repository.&lt;/li&gt;
&lt;li&gt;Responsibility for environment configuration is clearly delegated to the infrastructure layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuration is mounted at container startup. Therefore, any change to the &lt;code&gt;ConfigMap&lt;/code&gt; requires a pod restart, which may result in brief downtime depending on the application's restart time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fortunately, this limitation can be addressed by using &lt;a href="https://docs.spring.io/spring-cloud-kubernetes/reference/property-source-config/propertysource-reload.html" rel="noopener noreferrer"&gt;Spring Cloud Kubernetes PropertySource Reload&lt;/a&gt;, which enables dynamic configuration refresh without requiring a full pod restart.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>springboot</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
