How to Protect Laravel Environment Variables with Jenkins and Docker

August 2020 | John Binzak
Learn how to add and set environment variables in a Laravel Github Jenkins Docker CI/CD pipeline without saving them in source control.

Overview

We are going to add and set variables in a Laravel-Github-Jenkins-Docker CI/CD pipeline without saving them to Github. Essentially we are going to add to Jenkins, then have our Jenkinsfile add them to our Dockerfile, then have our Dockerfile add them to the .

Add Secrets to Jenkins

First step is to add a secret text to Jenkins. Navigate to and click . You will be prompted with a screen like the one below.

Jenkins Add Credentials Jenkins add credentials.

For this we want to use a . You will have to create a separate secret per variable that you need to hide. The value of the secret is obviously the value you want populated in the . For the , I suggest you use all lowercase characters with no spaces. We will use this in our Jenkinsfile.

For example, we can create a with an of for the database password.

Jenkins Secrets to Docker

Our next step is to read those secrets into our Jenkinsfile, so that we can pass them along to the Dockerfile. Let's say have a Jenkinsfile like below:

pipeline {
    environment {
        registry = ""
        registryCredential = ''
        prodImage = ''
    }
    agent none
    stages {
        stage('QA Testing') {
            agent {
                dockerfile {
                    filename ".docker/qa/Dockerfile"
                }
            }
            steps {
                echo 'Begin Testing...'
            }
        }
        stage('Build Prod') {
            agent {
                label 'master'
            }
            steps {
                echo 'Building Production....'

                script{
                    // build & push our docker image
                    prodImage = docker.build((registry+":latest"), "--file .docker/prod/Dockerfile .")
                    docker.withRegistry( '', registryCredential) {
                        prodImage.push("latest")
                    }
                }
            }
        }
        stage('Clean Up') {
            agent {
                label 'master'
            }
            steps {
                echo 'Cleaning....'
            }
        }
    }
}

Let's access the Jenkins secret via the and pass them to the Dockerfile as a :

pipeline {
    environment {
        registry = ""
        registryCredential = ''
        prodImage = ''
        production_database_password = credentials('production_database_password')
    }
    agent none
    stages {
        stage('QA Testing') {
            agent {
                dockerfile {
                    filename ".docker/qa/Dockerfile"
                }
            }
            steps {
                echo 'Begin Testing...'
            }
        }
        stage('Build Prod') {
            agent {
                label 'master'
            }
            steps {
                echo 'Building Production....'

                script{
                    // build & push our docker image
                    prodImage = docker.build((registry+":latest"),
                    "--build-arg production_database_password=${env.production_database_password} " +
                    "--file .docker/prod/Dockerfile .")
                    docker.withRegistry( '', registryCredential) {
                        prodImage.push("latest")
                    }
                }
            }
        }
        stage('Clean Up') {
            agent {
                label 'master'
            }
            steps {
                echo 'Cleaning....'
            }
        }
    }
}

Docker Build Arg

Now we can access the value for in the Dockerfile by simply using . It will best to still keep a basic without secrets in Git, otherwise you will have to write one from scratch via the Dockerfile. We then can either search & replace text in the basic or simply append the variables to the end. Below we simply appended the secret to the file.


# your docker build commands...

# build args
ARG production_database_password

# copy the basic .env without secrets
COPY .env.prod_without_secrets /var/www/html/.env

# set db
RUN echo "DB_PASSWORD=$production_database_password" >> /var/www/html/.env


# the rest of your docker build commands...

Summary

This is one way you can manage environment variables without having them under source control. This is really helpful for any type of secrets such as database credentials or third party API keys. You can also use this approach for multiple environments (i.e. QA/Staging). As quick summary here are the steps:

  • Add your variables as to Jenkins
  • Access those in the Jenkinsfile via the
  • Pass those in the Jenkinsfile to the Dockerfile as
  • Access those in the Dockerfile via
  • Append or search/replace those variables to a basic during the docker build