Learn Jenkins - Continuous Deployment (CD) to a Linux Server via SSH / Ansible
Episode 15 of 21

Learn Jenkins - Continuous Deployment (CD) to a Linux Server via SSH / Ansible

Automate deployment to Linux servers with secure SSH key injection, then level it up using Ansible and the ansiblePlaybook step from Jenkins.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 14 your pipeline ran tests, measured coverage, and enforced a Quality Gate with SonarQube. That means code that reached this point can be trusted. Only one decisive final step remains: sending that code to the production server.

Many teams still deploy manually — scp files, SSH into the server, restart the service, then hope everything works. That approach is slow, inconsistent, and once the team grows, it is impossible to trace who deployed what and when. Manual deploys are like delivering packages on foot when there is an automated courier: the result is the same, but the scale never grows.

In this episode we will build Continuous Deployment to a Linux server through two approaches: secure SSH key injection with the SSH Agent plugin, then full automation with Ansible using the ansiblePlaybook step.

Main Discussion

Tool Options for CD to a Linux Server

Broadly speaking, there are three common approaches:

ApproachPlugin/StepCharacteristics
SSH AgentsshagentInjects the SSH key into the environment, flexible for scp/ssh/rsync
Publish Over SSHsshPublisherSends files and executes commands through a configured SSH server
AnsibleansiblePlaybookDeclarative, idempotent deployment, can target many hosts at once

All three are valid, but Ansible wins as servers grow in number because a playbook describes the desired end state of a system, not the steps to get there.

SSH Key Injection with the SSH Agent Plugin

The SSH Agent plugin takes a private key from Jenkins Credentials and injects it into the SSH agent on the job's agent, only for the duration of the sshagent block. The key is never written to the workspace disk:

JenkinsDeploy via rsync + SSH
stage('Deploy via SSH') {
    steps {
        sshagent(['deploy-key']) {
            sh 'rsync -avz --delete dist/ deploy@prod-server:/var/www/my-app/'
            sh 'ssh deploy@prod-server "systemctl restart my-app"'
        }
    }
}

sshagent accepts a list of credential IDs stored in Jenkins. Inside the block, all SSH commands automatically use that key. The output remains visible in the console, but the key itself is never exposed.

Warning

Store the private key as an SSH Username with private key credential type in Jenkins; never place it as a file in the repository or workspace. A leaked key means anyone can get into the production server.

Alternative: Publish Over SSH

The Publish Over SSH plugin manages server connections in Jenkins's global configuration (host, username, key), then the sshPublisher step sends files and runs commands. It suits simple scenarios that only move artifacts:

JenkinsSend an artifact with sshPublisher
step([
    $class: 'Publisher',
    publishers: [
        [$class: 'SSHPublisherPlugin',
         configName: 'prod-server',
         transfers: [[sourceFiles: 'dist/**',
                      removePrefix: 'dist',
                      remoteDirectory: '/var/www/my-app']]]
    ]
])

Its syntax is class-based, so it is less pleasant to read than declarative steps. For that reason, most modern teams choose Ansible for more complex deployments.

Automating Deployment with Ansible

Ansible deploys by describing the desired end state via a playbook, and it is idempotent: running a playbook twice produces no double effect. To use it from Jenkins, install the Ansible plugin, then register an Ansible installation in Manage Jenkins > Tools — similar to how you register Maven.

Prepare an inventory that defines the production hosts. An example INI-style inventory with SSH-based hosts:

inventory/production.ini
[web]
prod-server ansible_host=203.0.113.10 ansible_user=deploy
 
[web:vars]
ansible_ssh_private_key_file=/tmp/deploy-key

A deployment playbook for a Java application can be as simple as:

ansible/deploy.yml
---
- name: Deploy aplikasi ke server produksi
  hosts: web
  become: true
  tasks:
    - name: Pastikan direktori target ada
      ansible.builtin.file:
        path: /opt/my-app
        state: directory
 
    - name: Salin artefak dari workspace
      ansible.builtin.copy:
        src: /tmp/my-app.jar
        dest: /opt/my-app/my-app.jar
        owner: myapp
        group: myapp
        mode: '0755'
 
    - name: Restart service aplikasi
      ansible.builtin.systemd_service:
        name: my-app
        state: restarted
 
    - name: Verifikasi health endpoint
      ansible.builtin.uri:
        url: http://localhost:8080/health
        status_code: 200

Then call that playbook from the pipeline with the ansiblePlaybook step:

JenkinsDeployment stage with Ansible
stage('Deploy dengan Ansible') {
    steps {
        ansiblePlaybook(
            playbook: 'ansible/deploy.yml',
            inventory: 'ansible/inventory/production.ini',
            extras: '--tags deploy'
        )
    }
}

The ansiblePlaybook step automatically picks the Ansible binary from the installation registered in Tools, runs the playbook with the specified inventory, and forwards extra arguments such as --tags to limit which tasks execute.

Tip

Make sure the build artifact is available in the agent workspace before the playbook runs — for example from an archiveArtifacts stage that is unstashed back. Ansible sends files from the machine running ansiblePlaybook, which is the Jenkins agent.

Deployment Security Best Practices

  • Create a dedicated deploy user with limited rights, not root.
  • Enable key-only login and disable password authentication in sshd.
  • Restrict allowed sudo commands via sudoers, for example only the application service restart.
  • Keep staging and production inventories separate; never mix them in one file.
  • Always verify service health after a deploy, not just the success of the file transfer.

Important

Automated deployment does not mean without control. For major changes, combine CD with a manual approval gate (input step) in front of the production deploy stage — as you learned in episode 6.

Conclusion

In episode 15 you automated deployment to a Linux server with secure SSH key injection via sshagent, learned the Publish Over SSH alternative for simple artifact transfers, and built declarative deployment with Ansible using the ansiblePlaybook step. You also got security best practices such as a dedicated deploy user, key-only login, and health verification after deploy.

Deploying to a Linux server is a solid foundation, but in the cloud-native era, applications are more often deployed to Kubernetes with the help of Helm.

In episode 16 we will discuss Continuous Deployment to Kubernetes & Helm — applying manifests with kubectl apply and automating releases with helm upgrade --install. See you there!

Learn Jenkins - Continuous Deployment (CD) to a Linux Server via SSH / Ansible | Learn Jenkins