The Real Multi-Server DevOps Guide – No Cap FR FR ๐Ÿ’…


Welcome to my blog! With Grok now free for everyone, I experimented with it to help me word this post in Gen Z slang. It’s so funny and cringe-worthy. XD.

Yo fam! Just a Gen Z dev out here messing around with servers and making this guide because why not? Finna deploy some containers and automate all the things, fr fr. Let’s get this bread! ๐Ÿ”ฅ

The Blueprint (What We’re Building) ๐Ÿ—๏ธ

We’re setting up two servers:

Server 1 (The Manager):

  • Docker (the foundation)
  • Portainer (the pretty UI)
  • Jenkins (the worker bee)

Server 2 (The WordPress Server):

  • Docker
  • WordPress + MySQL stack
  • Jenkins user (for automation)

The mission is to automate WordPress to update to the latest version from a jenkins pipeline.

Had some issues with the pipeline, so in this blog, you’ll see the errors and corrections as well.

Server 1: The Control Center Setup ๐ŸŽฎ

1. Docker Installation – The Foundation Arc ๐Ÿ’ช
# Update your system (keeping it fresh)
sudo apt update && sudo apt upgrade -y

# Get the prerequisites (the whole squad)
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common

# Add Docker's GPG key (security check)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Add Docker repo (the source of power)
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list

# Install Docker (the real MVP)
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Get that VIP access
sudo usermod -aG docker $USER

Log out and back in (yes bestie, you need to so that the usermod to take effect) ๐Ÿ‘‹

2. Portainer Installation – The UI Glow Up โœจ

Create a docker-compose.yml file.
I created mine in a specific folder : mkdir -p /docker/portainer
Then use your favourite editor : nano docker-compose.yml

version: '3'
services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: always
    security_opt:
      - no-new-privileges:true
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - portainer_data:/data
    ports:
      - 9000:9000
volumes:
  portainer_data:

Deploy it:

docker compose up -d

Setup steps:

1. Visit http://server1-ip:9000 (I’m using a private IP, mine is 192.168.1.110).

2. Create your admin account (make it secure bestie).

3. Choose Add Environment then choose Docker Standalone.

4. You’re in! Time to add Jenkins to the party

3. Jenkins Installation – The Automation Queen ๐Ÿ‘‘

We will deploy Jenkins via portainer :

1. Go to Stacks in Portainer

2. Add Stack (+ button in blue)

3. Name it ‘jenkins’ and paste this compose file for Jenkins

version: '3'
services:
  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    privileged: true
    user: root
    restart: unless-stopped
    ports:
      - 8080:8080
      - 50000:50000
    volumes:
      - jenkins_home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
volumes:
  jenkins_home:

4. Deploy the stack (at the end of the page)

Start the Jenkins Setup:

1. Get that initial password:

docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

2. Visit http://server1-ip:8080 (mine is 192.168.1.110)

3. Install suggested plugins

4. Create admin user (secure it like your spotify password)

Install this extra plugin (essential for ssh):

  • SSH Agent

Go in: Dashboard > Manage Jenkins > Plugins

Now in available plugins, search for the plugin mentioned above and install it :

Server 2: The WordPress Server Setup ๐ŸŒ

1. Docker Installation

Just like Server 1, download Docker as well.

2. Jenkins User Setup – The Access Manager ๐Ÿ”‘
# Create jenkins user
sudo useradd -m -s /bin/bash jenkins

# Add jenkins user to docker group
sudo usermod -aG docker jenkins

# Set a password
sudo passwd jenkins

# Switch to jenkins user
su - jenkins

# Generate SSH key pair(no need to put a passphrase)
ssh-keygen -t rsa -b 4096 -f ~/.ssh/jenkins_key

# Add public key to authorized_keys
cat ~/.ssh/jenkins_key.pub >> ~/.ssh/authorized_keys

# Set proper permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/jenkins_key

# Copy the private key (you'll need this for Jenkins)
cat ~/.ssh/jenkins_key
3. WordPress Stack Setup – The Content Kingdom ๐Ÿ‘‘

You can install portainer on this server but for now, we’ll stick to a docker-compose.yml directly.

Ight bet, let’s break down this docker-compose file – it’s literally the blueprint for our WordPress setup, no cap:

version: '3.1'
services:
  wordpress:
    image: wordpress
    restart: always
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wordpress:/var/www/html

  db:
    image: mysql:8.0
    restart: always
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_ROOT_PASSWORD: 1234
    volumes:
      - db:/var/lib/mysql

volumes:
  wordpress:
  db:

โš ๏ธ Don’t forget to change your Database and WordPress name, username, password and root password(mine is only for an example).

The tea on what’s happening here:

  1. We’re setting up two containers that are besties fr fr:
    • WordPress (the main character) – handles your website
    • MySQL (the memory keeper) – stores all your data
  2. The WordPress container is giving us:
    • Port 8080 so we can access our site (HTTP traffic and all that)
    • Some environment variables spilling the tea about the database connection
    • A volume to save all our files (ain’t nobody trying to lose their uploads)
  3. The MySQL container is coming through with:
    • Version 8.0 because we stay updated
    • Its own storage (volume) for all that precious data
    • A whole setup for our database user and passwords
  4. The volumes part? That’s where we store the goods:
    • wordpress: keeps your themes, plugins, and uploads safe
    • db: saves all your database info
    • Both stay intact even if you delete the containers (we love a backup)

To get this party started:

# Create a directory for our WordPress stack
mkdir -p /docker/wordpress-stack && cd /docker/wordpress-stack

# Save the compose file
nano docker-compose.yml  # paste that code above

# Fire it up bestie
docker compose up -d

Once it’s running, hit up http://server2-ip:8080(my server2 IP is 192.168.1.147) and you’ll see the WordPress setup screen. It’s giving main character energy fr fr! ๐Ÿ’…

Pro tip: Change them passwords before going live bestie, we ain’t trying to get hacked! ๐Ÿ”

IT WORKS !!

Back to Server 1: Jenkins Credentials Setup ๐Ÿ”

1. SSH Credentials
  1. Go to Manage Jenkins โ†’ Credentials โ†’ System โ†’ Global credentials โ†’ Add Credentials
  2. Set up SSH key:
    • Kind: SSH Username with private key
    • ID: sshkey-jenkins
    • Description: SSH key for Server 2 access
    • Username: jenkins
    • Private Key: Enter directly
    • Paste the private key from Server 2 (the one we copied earlier)
2. MySQL Credentials
  1. Go to Manage Jenkins โ†’ Credentials โ†’ System โ†’ Global credentials โ†’ Add Credentials
  2. Set up MySQL user:
    • Kind: Username with password
    • ID: mysql-credentials
    • Description: MySQL access for WordPress
    • Username: exampleuser (same as in docker-compose)
    • Password: examplepass (same as in docker-compose)

The Pipeline – The Main Character ๐ŸŒŸ

Ight, let’s set up that pipeline in Jenkins real quick:

1. Create that new pipeline:

  • Hit up Jenkins main page
  • Click “New Item” at the top left
  • Give it a name (like “wordpress-update-pipeline”)
  • Choose “Pipeline” project
  • Click “OK” (we valid)

2. In the pipeline config:

  • Scroll down to the “Pipeline” section
  • Under “Definition” choose “Pipeline script”
  • Drop our pipeline code in the “Script” box (the one we have below)
  • Don’t forget to change REMOTE_IP to your Server 2’s IP
  • Hit “Save” and we out

Tip: In General, I like to check these 2 boxes:

Create a new Pipeline in Jenkins and use this code (the automation bestie):

pipeline {
    agent any
    environment {
        SSH_CREDENTIALS = credentials('sshkey-jenkins')
        MYSQL_CREDENTIALS = credentials('mysql-credentials')
        REMOTE_USER = 'jenkins'
        REMOTE_IP = '192.168.1.147'
        WORDPRESS_CONTAINER = 'wordpress-stack-wordpress-1'
        WORDPRESS_DB_CONTAINER = 'wordpress-stack-db-1'
        WORDPRESS_DB = 'exampledb'
    }

    stages {
        stage('Backup WordPress') {
            steps {
                sshagent(['sshkey-jenkins']) {
                    script {
                        def retryCount = 3
                        def retryDelay = 10
                        
                        for (int i = 0; i < retryCount; i++) {
                            try {
                                sh '''
                                    echo 'Stopping WordPress container...'
                                    ssh ${REMOTE_USER}@${REMOTE_IP} "docker stop ${WORDPRESS_CONTAINER}"

                                    echo 'Dumping WordPress database...'
                                    ssh ${REMOTE_USER}@${REMOTE_IP} "docker exec ${WORDPRESS_DB_CONTAINER} mysqldump -u \${MYSQL_CREDENTIALS_USR} -p\${MYSQL_CREDENTIALS_PSW} ${WORDPRESS_DB} > ~/wordpress_backup.sql"

                                    echo 'Backing up WordPress files...'
                                    ssh ${REMOTE_USER}@${REMOTE_IP} "tar -czf ~/wordpress_files_backup.tar.gz -C /var/www/html ."

                                    echo 'Copying backups to Jenkins workspace...'
                                    scp ${REMOTE_USER}@${REMOTE_IP}:~/wordpress_backup.sql .
                                    scp ${REMOTE_USER}@${REMOTE_IP}:~/wordpress_files_backup.tar.gz .
                                '''
                                break
                            } catch (Exception e) {
                                if (i < retryCount - 1) {
                                    echo "Error during backup, retrying in ${retryDelay} seconds..."
                                    sleep(retryDelay)
                                } else {
                                    echo "Maximum retries reached, aborting backup."
                                    throw e
                                }
                            }
                        }
                    }
                }
            }
        }

        stage('Verify WordPress Image') {
            steps {
                script {
                    env.CURRENT_IMAGE_ID = sh(
                        script: "ssh -v ${REMOTE_USER}@${REMOTE_IP} \"docker inspect --format='{{.Image}}' ${WORDPRESS_CONTAINER}\"",
                        returnStdout: true
                    ).trim()

                    env.LATEST_IMAGE_ID = sh(
                        script: "ssh -v ${REMOTE_USER}@${REMOTE_IP} \"docker inspect --format='{{.Id}}' wordpress:latest\"",
                        returnStdout: true
                    ).trim()

                    echo "Current WordPress Image ID: ${env.CURRENT_IMAGE_ID}"
                    echo "Latest WordPress Image ID: ${env.LATEST_IMAGE_ID}"
                }
            }
        }

        stage('Pull Latest WordPress and Restart') {
            when {
                expression { env.CURRENT_IMAGE_ID != env.LATEST_IMAGE_ID }
            }
            steps {
                sshagent(['sshkey-jenkins']) {
                    script {
                        try {
                            sh '''
                                echo 'Pulling the latest WordPress image and restarting containers...'
                                ssh ${REMOTE_USER}@${REMOTE_IP} "
                                    cd /docker/wordpress-stack/
                                    docker compose down
                                    docker pull wordpress:latest
                                    docker compose up -d
                                "
                                # Wait for containers to start
                                sleep 30
                            '''
                            
                            // Additional verification after starting containers
                            def containerStatus = sh(
                                script: "ssh ${REMOTE_USER}@${REMOTE_IP} \"docker ps | grep wordpress\"",
                                returnStatus: true
                            )
                            
                            if (containerStatus != 0) {
                                error("WordPress container failed to start")
                            }
                        } catch (Exception e) {
                            echo "Failed to restart WordPress: ${e.message}"
                            currentBuild.result = 'FAILURE'
                            throw e
                        }
                    }
                }
            }
        }

        stage('Health Check') {
            when {
                expression { env.CURRENT_IMAGE_ID != env.LATEST_IMAGE_ID }
            }
            steps {
                script {
                    def healthStatus = sh(
                        script: "curl -o /dev/null -s -w '%{http_code}' http://${REMOTE_IP}:8080/",
                        returnStatus: true
                    )
                    if (healthStatus != 0) {
                        echo "Health check failed. Rolling back..."
                        currentBuild.result = 'FAILURE'
                        error("Health check failed")
                    } else {
                        echo "Health check passed. Upgrade successful!"
                    }
                }
            }
        }

        stage('Rollback on Failure') {
            when {
                expression { currentBuild.result == 'FAILURE' }
            }
            steps {
                sshagent(['sshkey-jenkins']) {
                    sh '''
                        echo 'Performing rollback...'
                        ssh ${REMOTE_USER}@${REMOTE_IP} "
                            echo 'Stopping current WordPress container...'
                            docker compose down

                            echo 'Rolling back to previous WordPress version...'
                            docker run -d --name wordpress --restart always \
                                -v /path/to/wordpress/html:/var/www/html \
                                wordpress:5.8.3

                            echo 'Rollback completed!'
                        "
                    '''
                }
            }
        }
    }

    post {
        success {
            script {
                if (env.CURRENT_IMAGE_ID == env.LATEST_IMAGE_ID) {
                    echo """*************************************************************
 WordPress is already at the latest version (${env.LATEST_IMAGE_ID})
 No upgrade was necessary at this time.
*************************************************************"""
                } else {
                    echo """*************************************************************
 WordPress successfully upgraded to the latest version!
 Previous Image ID: ${env.CURRENT_IMAGE_ID}
 New Image ID:      ${env.LATEST_IMAGE_ID}
*************************************************************"""
                }
            }
        }
        failure {
            echo """*************************************************************
 WordPress upgrade FAILED
 Rollback has been completed.
 Please check the build logs for detailed error information.
*************************************************************"""
        }
    }
}

โš ๏ธThe environment part is the most important in a pipeline (rename your variables if needed )

3. Test run that thing:

  • Click “Build Now” on the left
  • Watch the magic happen in “Stage View”
  • If it’s red, check them logs by clicking the stage
  • If it’s blue, you’re literally slaying rn

Now check Console Output to see if there’s any errors :

I had this error :

Here’s what’s happening:

  1. The pipeline starts correctly and sets up SSH credentials
  2. When trying to execute the SSH command to stop the WordPress container, it fails with “Host key verification failed”

Here are a few ways to fix this:

  1. The most secure approach is to add the host key to Jenkins’ known_hosts file. You can add this stage before your backup.
  2. Alternatively, you can modify your SSH commands to temporarily disable strict host checking (less secure, but useful for testing)

But I prefer the first fix (more secure). Add this part after the environment function(in the stages {}) :

stage('Setup SSH Known Hosts') {
    steps {
        sh '''
            mkdir -p ~/.ssh
            ssh-keyscan -H 192.168.1.147 >> ~/.ssh/known_hosts
        '''
    }
}

Let’s try building it again.

Now we got this error :

mysqldump: Error: 'Access denied; you need (at least one of) the PROCESS privilege(s) for this operation' when trying to dump tablespaces

I had to add –no-tablespaces in the mysqldump command :

ssh ${REMOTE_USER}@${REMOTE_IP} "docker exec ${WORDPRESS_DB_CONTAINER} mysqldump -u \${MYSQL_CREDENTIALS_USR} -p\${MYSQL_CREDENTIALS_PSW} ${WORDPRESS_DB} > ~/wordpress_backup.sql --no-tablespaces"

I also had to start the WordPress container :

docker start wordpress-stack-wordpress-1

Let’s try building it again.

New error, had to a new function, because of this error :

And now it works :

Here’s the updated pipeline with added pre-health check and more fixes :

pipeline {
    agent any
    environment {
        SSH_CREDENTIALS = credentials('sshkey-jenkins')
        MYSQL_CREDENTIALS = credentials('mysql-credentials')
        REMOTE_USER = 'jenkins'
        REMOTE_IP = '192.168.1.147'
        WORDPRESS_CONTAINER = 'wordpress-stack-wordpress-1'
        WORDPRESS_DB_CONTAINER = 'wordpress-stack-db-1'
        WORDPRESS_DB = 'exampledb'
        WORDPRESS_PATH = '/docker/wordpress-stack'
    }

    stages {
        stage('Setup SSH Known Hosts') {
            steps {
                sh '''
                    mkdir -p ~/.ssh
                    ssh-keyscan -H ${REMOTE_IP} >> ~/.ssh/known_hosts
                '''
            }
        }
		
		stage('Pre-backup Health Check') {
            steps {
                script {
                    def maxAttempts = 3
                    def waitTime = 10
                    def healthy = false

                    for (int i = 0; i < maxAttempts && !healthy; i++) {
                        try {
                            def healthStatus = sh(
                                script: "curl -o /dev/null -s -w '%{http_code}' http://${REMOTE_IP}:8080/",
                                returnStdout: true
                            ).trim()

                            if (healthStatus == '200') {
                                healthy = true
                                echo "WordPress is up and running!"
                            } else {
                                throw new Exception("WordPress returned HTTP ${healthStatus}")
                            }
                        } catch (Exception e) {
                            if (i < maxAttempts - 1) {
                                echo "Health check failed, attempt ${i + 1}/${maxAttempts}. Waiting ${waitTime} seconds before retry..."
                                // If WordPress is down, try to start it
                                sshagent(['sshkey-jenkins']) {
                                    sh """
                                        ssh ${REMOTE_USER}@${REMOTE_IP} '
                                            cd ${WORDPRESS_PATH}
                                            docker compose up -d
                                        '
                                    """
                                }
                                sleep(waitTime)
                            } else {
                                error "WordPress health check failed after ${maxAttempts} attempts: ${e.message}"
                            }
                        }
                    }
                }
            }
        }
		
        stage('Backup WordPress') {
            steps {
                sshagent(['sshkey-jenkins']) {
                    script {
                        def retryCount = 3
                        def retryDelay = 10
                        
                        for (int i = 0; i < retryCount; i++) {
                            try {
                                sh '''
                                    echo 'Ensuring backup directory exists...'
                                    ssh ${REMOTE_USER}@${REMOTE_IP} "[ ! -d ~/wordpress_backups ] && mkdir -p ~/wordpress_backups || echo 'Backup directory already exists'"

                                    echo 'Backing up WordPress files...'
                                    ssh ${REMOTE_USER}@${REMOTE_IP} "docker exec ${WORDPRESS_CONTAINER} tar -czf /tmp/wordpress_files_backup.tar.gz -C /var/www/html . && docker cp ${WORDPRESS_CONTAINER}:/tmp/wordpress_files_backup.tar.gz ~/wordpress_backups/"

                                    echo 'Dumping WordPress database...'
                                    ssh ${REMOTE_USER}@${REMOTE_IP} "docker exec ${WORDPRESS_DB_CONTAINER} mysqldump -u \${MYSQL_CREDENTIALS_USR} -p\${MYSQL_CREDENTIALS_PSW} ${WORDPRESS_DB} > ~/wordpress_backups/wordpress_backup.sql --no-tablespaces"

                                    echo 'Copying backups to Jenkins workspace...'
                                    scp ${REMOTE_USER}@${REMOTE_IP}:~/wordpress_backups/wordpress_backup.sql .
                                    scp ${REMOTE_USER}@${REMOTE_IP}:~/wordpress_backups/wordpress_files_backup.tar.gz .
                                    
                                    echo 'Stopping WordPress container...'
                                    ssh ${REMOTE_USER}@${REMOTE_IP} "docker stop ${WORDPRESS_CONTAINER}"
                                '''
                                break
                            } catch (Exception e) {
                                if (i < retryCount - 1) {
                                    echo "Error during backup, retrying in ${retryDelay} seconds..."
                                    sleep(retryDelay)
                                } else {
                                    echo "Maximum retries reached, aborting backup."
                                    throw e
                                }
                            }
                        }
                    }
                }
            }
        }

        stage('Verify WordPress Image') {
            steps {
                sshagent(['sshkey-jenkins']) {
                    script {
                        // Store current image ID before potential update
                        sh "ssh ${REMOTE_USER}@${REMOTE_IP} \"docker inspect ${WORDPRESS_CONTAINER} --format='{{.Image}}' > ~/current_image_id\""
                        
                        env.CURRENT_IMAGE_ID = sh(
                            script: "ssh ${REMOTE_USER}@${REMOTE_IP} \"docker inspect --format='{{.Image}}' ${WORDPRESS_CONTAINER}\"",
                            returnStdout: true
                        ).trim()

                        env.LATEST_IMAGE_ID = sh(
                            script: "ssh ${REMOTE_USER}@${REMOTE_IP} \"docker inspect --format='{{.Id}}' wordpress:latest\"",
                            returnStdout: true
                        ).trim()

                        echo "Current WordPress Image ID: ${env.CURRENT_IMAGE_ID}"
                        echo "Latest WordPress Image ID: ${env.LATEST_IMAGE_ID}"
                        
                        // If no update needed, restart the stopped container
                        if (env.CURRENT_IMAGE_ID == env.LATEST_IMAGE_ID) {
                            sh "ssh ${REMOTE_USER}@${REMOTE_IP} \"docker start ${WORDPRESS_CONTAINER}\""
                        }
                    }
                }
            }
        }

        stage('Pull Latest WordPress and Restart') {
            when {
                expression { env.CURRENT_IMAGE_ID != env.LATEST_IMAGE_ID }
            }
            steps {
                sshagent(['sshkey-jenkins']) {
                    script {
                        try {
                            sh '''
                                echo 'Pulling the latest WordPress image and restarting containers...'
                                ssh ${REMOTE_USER}@${REMOTE_IP} "
                                    cd ${WORDPRESS_PATH}
                                    docker compose down
                                    docker pull wordpress:latest
                                    docker compose up -d
                                "
                                # Wait for containers to start
                                sleep 30
                            '''
                            
                            // Additional verification after starting containers
                            def containerStatus = sh(
                                script: "ssh ${REMOTE_USER}@${REMOTE_IP} \"docker ps | grep ${WORDPRESS_CONTAINER}\"",
                                returnStatus: true
                            )
                            
                            if (containerStatus != 0) {
                                error("WordPress container failed to start")
                            }
                        } catch (Exception e) {
                            echo "Failed to restart WordPress: ${e.message}"
                            currentBuild.result = 'FAILURE'
                            throw e
                        }
                    }
                }
            }
        }

        stage('Health Check') {
            when {
                expression { env.CURRENT_IMAGE_ID != env.LATEST_IMAGE_ID }
            }
            steps {
                script {
                    def maxRetries = 5
                    def retryDelay = 10
                    def healthy = false

                    for (int i = 0; i < maxRetries && !healthy; i++) {
                        def healthStatus = sh(
                            script: "curl -o /dev/null -s -w '%{http_code}' http://${REMOTE_IP}:8080/",
                            returnStatus: true
                        )
                        if (healthStatus == 0) {
                            healthy = true
                            echo "Health check passed!"
                        } else {
                            if (i < maxRetries - 1) {
                                echo "Health check failed, retrying in ${retryDelay} seconds..."
                                sleep(retryDelay)
                            } else {
                                echo "Health check failed after ${maxRetries} attempts. Rolling back..."
                                currentBuild.result = 'FAILURE'
                                error("Health check failed")
                            }
                        }
                    }
                }
            }
        }

        stage('Rollback on Failure') {
            when {
                expression { currentBuild.result == 'FAILURE' }
            }
            steps {
                sshagent(['sshkey-jenkins']) {
                    script {
                        // Get the stored previous image ID
                        def previousImageId = sh(
                            script: "ssh ${REMOTE_USER}@${REMOTE_IP} 'cat ~/current_image_id'",
                            returnStdout: true
                        ).trim()
                        
                        sh """
                            echo 'Performing rollback...'
                            ssh ${REMOTE_USER}@${REMOTE_IP} "
                                cd ${WORDPRESS_PATH}
                                echo 'Stopping current containers...'
                                docker compose down
                                
                                echo 'Restoring database...'
                                docker compose up -d db
                                sleep 10
                                docker exec ${WORDPRESS_DB_CONTAINER} mysql -u \${MYSQL_CREDENTIALS_USR} -p\${MYSQL_CREDENTIALS_PSW} ${WORDPRESS_DB} < ~/wordpress_backups/wordpress_backup.sql
                                
                                echo 'Restoring WordPress files and container...'
                                docker compose up -d wordpress
                                docker cp ~/wordpress_backups/wordpress_files_backup.tar.gz ${WORDPRESS_CONTAINER}:/tmp/
                                docker exec ${WORDPRESS_CONTAINER} tar -xzf /tmp/wordpress_files_backup.tar.gz -C /var/www/html
                                
                                echo 'Rollback completed!'
                            "
                        """
                    }
                }
            }
        }
    }

    post {
        success {
            script {
                if (env.CURRENT_IMAGE_ID == env.LATEST_IMAGE_ID) {
                    echo """*************************************************************
 WordPress is already at the latest version (${env.LATEST_IMAGE_ID})
 No upgrade was necessary at this time.
*************************************************************"""
                } else {
                    echo """*************************************************************
 WordPress successfully upgraded to the latest version!
 Previous Image ID: ${env.CURRENT_IMAGE_ID}
 New Image ID:      ${env.LATEST_IMAGE_ID}
*************************************************************"""
                }
            }
        }
        failure {
            echo """*************************************************************
 WordPress upgrade FAILED
 Rollback has been completed.
 Please check the build logs for detailed error information.
*************************************************************"""
        }
        always {
            echo "Pipeline completed at ${new Date().format('yyyy-MM-dd HH:mm:ss')}"
        }
    }
}

Here’s the console output of the fixed pipeline if you wish to see how it worked :

The End Game ๐Ÿ†

You’ve just:

  • Set up two whole servers (period)
  • Made Jenkins and Portainer besties
  • Created a pipeline that actually works
  • Automated WordPress updates (work smarter not harder)

Remember:

  • Keep them passwords strong (no “password123” allowed)
  • Test your backups (trust issues are good actually)
  • Monitor them logs (stay woke)
  • Update your documentation (because future you will thank you)

Now go touch grass while your automation does the work! ๐ŸŒฑ


Leave a Reply

Your email address will not be published. Required fields are marked *

Verified by MonsterInsights