Friday, 14 November 2025

Docker - Volume - Setup using named volumes

 A Docker Volume is the preferred mechanism for persisting data generated by and used by Docker containers.

Containers are designed to be ephemeral (temporary and disposable). Any data written directly to a container's filesystem layer is lost when the container is removed. A volume solves this by providing a storage location that exists on the host machine but is managed by Docker, ensuring the data remains intact even if the containers are stopped, removed, or replaced.

in Simple terms, Docker Volume is a special storage mechanism in Docker to store data outside the container’s filesystem, so the data is not lost when a container stops, restarts, or is removed.

By default, the data modified or created within a container is stored in container's writable layer 

Key Characteristics

  • Persistence: Data stored in a volume survives the removal and recreation of the container that created it.

  • Docker Management: Volumes are created and managed via the Docker CLI (docker volume create, docker volume ls, etc.). On Linux, they are typically stored under a directory managed by Docker (usually /var/lib/docker/volumes/).

  • Data Sharing: The same volume can be safely mounted into multiple running containers simultaneously, allowing them to share data.

  • Portability: Volumes are independent of the host's specific directory structure, making them easier to migrate and back up across different environments (Linux, Windows, macOS).

🗃️ Types of Docker Storage Mounts

Docker provides a few types of storage mounts, but Volumes are generally recommended for production data

Mount TypeDescriptionBest Use Case
Named VolumesThe most common type. They are created with a specific name (e.g., db_data) and fully managed by Docker.Production data persistence (databases, application state, logs).
Anonymous VolumesSimilar to named volumes but are not given an explicit name; Docker assigns them a unique random ID.When data persistence is needed, but the volume doesn't need to be referenced or reused explicitly.
Bind MountsMaps a specific file or directory on the host machine (defined by an absolute path) directly into the container. Docker does not manage the host location.Development work, where you need to immediately see code changes reflected in the container.
Jenkins will be our example application for illustrating volume persistence.

[root@devopsvm01 ~]#  docker images
REPOSITORY                  TAG         IMAGE ID       CREATED                  SIZE
nginx                       latest      d261fd19cb63   Less than a second ago   152MB
mahekarthya/my-nginx-site   version-1   5fbef17336dd   3 hours ago              48.3MB
ubuntu                      latest      97bed23a3497   4 weeks ago              78.1MB
hello-world                 latest      1b44b5a3e06a   2 months ago             10.1kB
[root@devopsvm01 ~]#
[root@devopsvm01 ~]#

Pull the latest Jenkins image.

[root@devopsvm01 ~]# docker pull jenkins/jenkins:2.528.2-lts-jdk21
2.528.2-lts-jdk21: Pulling from jenkins/jenkins
13cc39f8244a: Pull complete
dc2a77f462ea: Pull complete
33300af18dd0: Pull complete
c27509c3e53b: Pull complete
e4beac64dffa: Pull complete
a37b858bb47a: Pull complete
744b4792e083: Pull complete
05a7d9a8b608: Pull complete
8d2a75b252b2: Pull complete
65e4ba8066bc: Pull complete
5dc07232677a: Pull complete
7718ff514022: Pull complete
Digest: sha256:7b1c378278279c8688efd6168c25a1c2723a6bd6f0420beb5ccefabee3cc3bb1
Status: Downloaded newer image for jenkins/jenkins:2.528.2-lts-jdk21
docker.io/jenkins/jenkins:2.528.2-lts-jdk21
[root@devopsvm01 ~]#
[root@devopsvm01 ~]#  docker images
REPOSITORY                  TAG                 IMAGE ID       CREATED                  SIZE
jenkins/jenkins             2.528.2-lts-jdk21   aa2bbbd632f3   Less than a second ago   490MB
nginx                       latest              d261fd19cb63   Less than a second ago   152MB
mahekarthya/my-nginx-site   version-1           5fbef17336dd   3 hours ago              48.3MB
ubuntu                      latest              97bed23a3497   4 weeks ago              78.1MB
hello-world                 latest              1b44b5a3e06a   2 months ago             10.1kB
[root@devopsvm01 ~]#

Create a volume.

[root@devopsvm01 ~]# docker volume ls
DRIVER    VOLUME NAME
[root@devopsvm01 ~]#
[root@devopsvm01 ~]# docker volume create Jenkins-Vol
Jenkins-Vol
[root@devopsvm01 ~]# docker volume ls
DRIVER    VOLUME NAME
local     Jenkins-Vol
[root@devopsvm01 ~]# docker volume inspect Jenkins-Vol
[
    {
        "CreatedAt": "2025-11-03T15:03:30+05:30",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/Jenkins-Vol/_data",
        "Name": "Jenkins-Vol",
        "Options": null,
        "Scope": "local"
    }
]
[root@devopsvm01 ~]# cd /var/lib/docker/volumes/Jenkins-Vol/_data
[root@devopsvm01 _data]# ls -lrt
total 0
[root@devopsvm01 _data]#

By default, Docker stores volumes in the directory /var/lib/docker/volumes/ on the host machine.

When you created the named volume Jenkins-Vol, Docker automatically created and manages the corresponding directory on your host file system at the path specified by the Mountpoint field:

/var/lib/docker/volumes/Jenkins-Vol/_data

This directory on your host is where all the persistent data for your Jenkins container will be stored.


Spin up a new Jenkin container using the newly created volume.


Before creating a new container, we need to map Jenkins home to a Docker volume. For that, we must know the default Jenkins home directory

[root@devopsvm01 _data]# docker inspect  jenkins/jenkins:2.528.2-lts-jdk21 | grep -i '"JENKINS_HOME'
                "JENKINS_HOME=/var/jenkins_home",
[root@devopsvm01 _data]#

So Jenkins store the workspace in /var/jenkins_home. All Jenkins data lives in there - including plugins and configuration.


docker run --name Jenkins1 -v Jenkins-Vol:/var/jenkins_home -p 8080:8080 -p 50000:50000 jenkins/jenkins:2.528.2-lts-jdk21

































[root@devopsvm01 _data]# docker ps -a | grep -i "Up"
cd6834b2e669   jenkins/jenkins:2.528.2-lts-jdk21   "/usr/bin/tini -- /u…"   50 minutes ago      Up 50 minutes                  0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp, 0.0.0.0:50000->50000/tcp, [::]:50000->50000/tcp   Jenkins1
[root@devopsvm01 _data]#

The persistent Jenkins configuration and plugin data are now stored in the host directory, /var/lib/docker/volumes/Jenkins-Vol/_data.

[root@devopsvm01 _data]# pwd
/var/lib/docker/volumes/Jenkins-Vol/_data
[root@devopsvm01 _data]# ls -lhrt
total 52K
drwxr-xr-x. 10 maheshpayyan maheshpayyan 4.0K Nov  3 15:38 war
-rw-r--r--.  1 maheshpayyan maheshpayyan   64 Nov  3 15:38 secret.key
-rw-r--r--.  1 maheshpayyan maheshpayyan    0 Nov  3 15:38 secret.key.not-so-secret
drwxr-xr-x.  2 maheshpayyan maheshpayyan    6 Nov  3 15:38 plugins
-rw-r--r--.  1 maheshpayyan maheshpayyan  171 Nov  3 15:38 jenkins.telemetry.Correlator.xml
drwxr-xr-x.  2 maheshpayyan maheshpayyan   24 Nov  3 15:38 userContent
drwxr-xr-x.  3 maheshpayyan maheshpayyan   84 Nov  3 15:38 users
drwxr-xr-x.  2 maheshpayyan maheshpayyan   67 Nov  3 15:38 updates
-rw-r--r--.  1 maheshpayyan maheshpayyan  179 Nov  3 16:07 jenkins.model.JenkinsLocationConfiguration.xml
-rw-r--r--.  1 maheshpayyan maheshpayyan    7 Nov  3 16:08 jenkins.install.UpgradeWizard.state
drwxr-xr-x.  3 maheshpayyan maheshpayyan   21 Nov  3 16:26 workspace
drwx------.  2 maheshpayyan maheshpayyan 4.0K Nov  3 16:26 secrets
-rw-r--r--.  1 maheshpayyan maheshpayyan  258 Nov  3 16:27 queue.xml.bak
-rw-r--r--.  1 maheshpayyan maheshpayyan  216 Nov  3 16:31 copy_reference_file.log
-rw-r--r--.  1 maheshpayyan maheshpayyan  156 Nov  3 16:31 hudson.model.UpdateCenter.xml
-rw-r--r--.  1 maheshpayyan maheshpayyan    7 Nov  3 16:31 jenkins.install.InstallUtil.lastExecVersion
-rw-r--r--.  1 maheshpayyan maheshpayyan 1.1K Nov  3 16:31 nodeMonitors.xml
-rw-r--r--.  1 maheshpayyan maheshpayyan 1.7K Nov  3 16:31 config.xml
-rw-r--r--.  1 maheshpayyan maheshpayyan  258 Nov  3 16:44 queue.xml
drwxr-xr-x.  2 maheshpayyan maheshpayyan    6 Nov  3 16:48 jobs
[root@devopsvm01 _data]#


Note down the admin password and login to Jenkins console 


























Select Plugins to install option























Select none and finish the installation.

























Skip and continue as admin


























Finish 
























Create a sample job on this container 























Give some name 

























Under build step, select execute a shell 

































Let's build the job

























Job ran successful.



















Now spin up another instance of Jenkins using the same volume that we used earlier 

docker run --name Jenkins2 -v Jenkins-Vol:/var/jenkins_home -p 9080:8080 -p 60000:50000 jenkins/jenkins:2.528.2-lts-jdk21


[root@devopsvm01 ~]# docker ps -a | grep -i "Up"
cd6834b2e669   jenkins/jenkins:2.528.2-lts-jdk21   "/usr/bin/tini -- /u…"   52 minutes ago      Up 52 minutes                  0.0.0.0:8080->8080/tcp, [::]:                8080->8080/tcp, 0.0.0.0:50000->50000/tcp, [::]:50000->50000/tcp   Jenkins1
[root@devopsvm01 ~]#
[root@devopsvm01 ~]# docker run --name Jenkins2 -v Jenkins-Vol:/var/jenkins_home -p 9080:8080 -p 60000:50000 jenkins/jenkins:2.528.2-lts-jdk21

Running from: /usr/share/jenkins/jenkins.war
webroot: /var/jenkins_home/war
2025-11-03 11:01:09.787+0000 [id=1]     INFO    winstone.Logger#logInternal: Beginning extraction from war file
2025-11-03 11:01:10.188+0000 [id=1]     WARNING o.e.j.ee9.nested.ContextHandler#setContextPath: Empty contextPath
2025-11-03 11:01:10.557+0000 [id=1]     INFO    org.eclipse.jetty.server.Server#doStart: jetty-12.0.25; built: 2025-08-11T23:52:37.219Z; git: a862b76d8372e2                4205765182d9ae1d1d333ce2ea; jvm 21.0.9+10-LTS
2025-11-03 11:01:13.624+0000 [id=1]     INFO    o.e.j.e.w.StandardDescriptorProcessor#visitServlet: NO JSP Support for /, did not find org.eclipse.jetty.ee9.jsp.JettyJspServlet
2025-11-03 11:01:14.402+0000 [id=1]     INFO    o.e.j.s.DefaultSessionIdManager#doStart: Session workerName=node0
2025-11-03 11:01:16.804+0000 [id=1]     INFO    hudson.WebAppMain#contextInitialized: Jenkins home directory: /var/jenkins_home found at: EnvVars.masterEnvVars.get("JENKINS_HOME")
2025-11-03 11:01:17.687+0000 [id=1]     INFO    o.e.j.s.handler.ContextHandler#doStart: Started oeje9n.ContextHandler$CoreContextHandler@1c12f3ee{Jenkins v2.528.2,/,b=file:///var/jenkins_home/war/,a=AVAILABLE,h=oeje9n.ContextHandler$CoreContextHandler$CoreToNestedHandler@6d467c87{STARTED}}
2025-11-03 11:01:17.830+0000 [id=1]     INFO    o.e.j.server.AbstractConnector#doStart: Started ServerConnector@2b97a809{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
2025-11-03 11:01:17.932+0000 [id=1]     INFO    org.eclipse.jetty.server.Server#doStart: Started oejs.Server@4a883b15{STARTING}[12.0.25,sto=0] @11748ms
2025-11-03 11:01:17.953+0000 [id=25]    INFO    winstone.Logger#logInternal: Winstone Servlet Engine running: controlPort=disabled
2025-11-03 11:01:18.771+0000 [id=24]    INFO    jenkins.model.Jenkins#<init>: Starting version 2.528.2
2025-11-03 11:01:20.076+0000 [id=31]    INFO    jenkins.InitReactorRunner$1#onAttained: Started initialization
2025-11-03 11:01:20.401+0000 [id=30]    INFO    jenkins.InitReactorRunner$1#onAttained: Listed all plugins
2025-11-03 11:01:26.311+0000 [id=30]    INFO    jenkins.InitReactorRunner$1#onAttained: Prepared all plugins
2025-11-03 11:01:26.330+0000 [id=30]    INFO    jenkins.InitReactorRunner$1#onAttained: Started all plugins
2025-11-03 11:01:26.341+0000 [id=31]    INFO    jenkins.InitReactorRunner$1#onAttained: Augmented all extensions
2025-11-03 11:01:28.337+0000 [id=31]    INFO    jenkins.InitReactorRunner$1#onAttained: System config loaded
2025-11-03 11:01:28.346+0000 [id=30]    INFO    jenkins.InitReactorRunner$1#onAttained: System config adapted
2025-11-03 11:01:28.473+0000 [id=30]    INFO    jenkins.InitReactorRunner$1#onAttained: Loaded all jobs
2025-11-03 11:01:28.477+0000 [id=31]    INFO    jenkins.InitReactorRunner$1#onAttained: Configuration for all jobs updated
2025-11-03 11:01:29.538+0000 [id=31]    INFO    jenkins.InitReactorRunner$1#onAttained: Completed initialization
2025-11-03 11:01:30.021+0000 [id=24]    INFO    hudson.lifecycle.Lifecycle#onReady: Jenkins is fully up and running


When you setup second  Jenkins using the same volume, you are not being prompted for plugin setup or a new password when starting Jenkins2 because you used the same volume, Jenkins-Vol, for both containers.

Verify both Jenkins uses same volume.

[root@devopsvm01 _data]# docker ps --format "{{.Names}}\t{{.Mounts}}"
Jenkins2        Jenkins-Vol
Jenkins1        Jenkins-Vol
[root@devopsvm01 _data]# 

💾 Why the Setup Was Skipped

The Jenkins-Vol volume contains all the persistent data from the first run of Jenkins (Jenkins1), including:

  1. Configuration Files: All settings, jobs, and user data.

  2. Plugins: The list of plugins you selected and installed.

  3. Security Files: Crucially, the initial administrative user that was set up and the file containing the final admin password.

When you ran Jenkins2 with the same volume, Jenkins saw the existing home directory (/var/jenkins_home) populated with a complete, fully configured setup. Therefore, it skipped the initial setup wizard (plugin installation and initial password generation) and immediately started using the configuration and security data already saved in the volume.


How to Access the Second Jenkins Instance

Since Jenkins2 is using the settings from Jenkins1, you need to access it using the new host ports you defined, and log in with the same administrator credentials you created during the first setup.

  1. Access the new Jenkins URL:

    http://<Your_VM_IP_Address>:9080

Let's login and run the job that we created from Jenkins1





























Check the job status and try to run 


































We are able to run the job from Jenkins2 as both Jenkins1 and Jenkins2 are sharing the same home.



No comments:

Post a Comment

Building a Safer PostgreSQL CI/CD Pipeline with GitHub Actions: Dev → PR Review → Test Promotion

In my previous post, we explored a simple push-to-main deployment strategy . While functional, that model is not considered an industry best...