Skip to main content

RSync Deploy Mittwald

The ansible-craft-deploy job is used to deploy Craft CMS applications to Mittwald hosting using Ansible and rsync. It synchronizes application files to Mittwald servers via SSH.

Overview

This job:

  • Uses Ansible to orchestrate deployments to Mittwald hosting
  • Synchronizes application files using rsync
  • Clones a specific version of the Ansible module from GitLab
  • Configures SSH access for remote server management
  • Handles environment-specific .htaccess files
  • Supports environment-specific Mittwald configuration

Variables

The following variables can be configured:

VariableDescriptionDefaultRequired
ANSIBLE_MODULE_VERSIONVersion/tag of the Ansible module to usev0.0.118No
ANSIBLE_MODULE_PATHGitLab path to the Ansible module repositorywelance/platform/infrastructure/modules/ansible.gitNo
ROLE_DIRDirectory where Ansible role is locatedansible/roles/craft-rsync-deployNo
APP_ENVIRONMENTTarget environment''Yes

Environment-Specific Variables

The job dynamically resolves environment-specific variables based on APP_ENVIRONMENT:

  • MITTWALD_ID_{ENVIRONMENT}: Mittwald account/ID for the environment (e.g., MITTWALD_ID_DEVELOP, MITTWALD_ID_STAGING, MITTWALD_ID_PRODUCTION)
  • MITTWALD_PATH_{ENVIRONMENT}: Deployment path on Mittwald server (e.g., MITTWALD_PATH_DEVELOP)
  • MITTWALD_HOST_{ENVIRONMENT}: Mittwald server hostname (e.g., MITTWALD_HOST_DEVELOP)

Variable Details

  • ANSIBLE_MODULE_VERSION: The Git tag or branch of the Ansible module repository to use (e.g., v0.0.118)
  • ANSIBLE_MODULE_PATH: The GitLab project path to the Ansible module repository
  • ROLE_DIR: Path to the Ansible role within the cloned module
  • APP_ENVIRONMENT: Target environment. Options:
    • 'develop' - Development environment
    • 'staging' - Staging environment
    • 'production' - Production environment

GitLab CI/CD Variables

The job uses the following built-in GitLab CI/CD variables:

  • DOCKER_HOST: Docker daemon connection (set to tcp://docker:2376)
  • DOCKER_TLS_CERTDIR: Docker TLS certificate directory (set to "/certs")
  • CI_PROJECT_DIR: Full path where the repository is cloned

Required Secrets

The following secrets must be configured in GitLab CI/CD variables:

  • ACCESS_TOKEN: GitLab OAuth token with access to clone the Ansible module repository
  • ANSIBLE_SSH_ID_RSA: SSH private key content for authenticating to Mittwald servers
  • MITTWALD_ID_DEVELOP, MITTWALD_ID_STAGING, MITTWALD_ID_PRODUCTION: Mittwald account IDs per environment
  • MITTWALD_PATH_DEVELOP, MITTWALD_PATH_STAGING, MITTWALD_PATH_PRODUCTION: Deployment paths per environment
  • MITTWALD_HOST_DEVELOP, MITTWALD_HOST_STAGING, MITTWALD_HOST_PRODUCTION: Server hostnames per environment

Usage

Basic Usage

variables:
ANSIBLE_MODULE_VERSION: v0.0.118
ANSIBLE_MODULE_PATH: welance/platform/infrastructure/modules/ansible.git
APP_ENVIRONMENT: 'develop'

.ansible-craft-deploy:
image: willhallonline/ansible:2.16.4-ubuntu-22.04
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
ROLE_DIR: ansible/roles/craft-rsync-deploy
before_script:
- eval "MITTWALD_ID=\${MITTWALD_ID_$(echo $APP_ENVIRONMENT | tr '[:lower:]' '[:upper:]')}"
- eval "MITTWALD_PATH=\${MITTWALD_PATH_$(echo $APP_ENVIRONMENT | tr '[:lower:]' '[:upper:]')}"
- eval "MITTWALD_HOST=\${MITTWALD_HOST_$(echo $APP_ENVIRONMENT | tr '[:lower:]' '[:upper:]')}"
- echo $MITTWALD_ID
- echo $MITTWALD_PATH
- echo $MITTWALD_HOST
- 'which ssh-agent || ( apt-get update -qy && apt-get install openssh-client -qqy )'
- apt-get update -qy && apt-get install rsync -qqy
- mkdir -p ~/.ssh/ && touch ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
- cat $ANSIBLE_SSH_ID_RSA > ~/.ssh/id_rsa
- echo "clone ansible module version "
- git clone --depth 1 --branch $ANSIBLE_MODULE_VERSION https://oauth2:[email protected]/$ANSIBLE_MODULE_PATH
- mv .env.$APP_ENVIRONMENT .env
- if [ -f ./web/.$APP_ENVIRONMENT.htaccess ]; then rm -f ./web/.htaccess && mv ./web/.$APP_ENVIRONMENT.htaccess ./web/.htaccess || true; fi
- rm -f ./web/.*.htaccess
- cd ansible
script:
- sed -i "s/\$MITTWALD_HOST/${MITTWALD_HOST}/g" playbook-mittwald.yml
- cat playbook-mittwald.yml
- ANSIBLE_REMOTE_TEMP=$MITTWALD_PATH ansible-playbook playbook-mittwald.yml -i hosts-mittwald --extra-vars "ansible_ssh_extra_args='-o StrictHostKeyChecking=no'" --private-key="~/.ssh/id_rsa" --extra-vars "ENVIRONMENT=$APP_ENVIRONMENT craft_src_path=$CI_PROJECT_DIR/ craft_www_path=$MITTWALD_PATH"

Job Details

Before Script

The before_script section performs the following setup:

  1. Resolve environment-specific variables:

    eval "MITTWALD_ID=\${MITTWALD_ID_$(echo $APP_ENVIRONMENT | tr '[:lower:]' '[:upper:]')}"
    eval "MITTWALD_PATH=\${MITTWALD_PATH_$(echo $APP_ENVIRONMENT | tr '[:lower:]' '[:upper:]')}"
    eval "MITTWALD_HOST=\${MITTWALD_HOST_$(echo $APP_ENVIRONMENT | tr '[:lower:]' '[:upper:]')}"
    • Dynamically resolves environment-specific Mittwald variables
    • Converts environment name to uppercase (e.g., developDEVELOP)
    • Example: For APP_ENVIRONMENT=develop, resolves MITTWALD_ID_DEVELOP
  2. Display resolved variables (for debugging):

    echo $MITTWALD_ID
    echo $MITTWALD_PATH
    echo $MITTWALD_HOST
  3. Install SSH client (if not available):

    which ssh-agent || ( apt-get update -qy && apt-get install openssh-client -qqy )
  4. Install rsync:

    apt-get update -qy && apt-get install rsync -qqy
  5. Configure SSH key:

    mkdir -p ~/.ssh/ && touch ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
    cat $ANSIBLE_SSH_ID_RSA > ~/.ssh/id_rsa
  6. Clone Ansible module:

    git clone --depth 1 --branch $ANSIBLE_MODULE_VERSION https://oauth2:[email protected]/$ANSIBLE_MODULE_PATH
  7. Prepare environment file:

    mv .env.$APP_ENVIRONMENT .env
    • Moves environment-specific .env file to .env
  8. Handle .htaccess files:

    if [ -f ./web/.$APP_ENVIRONMENT.htaccess ]; then 
    rm -f ./web/.htaccess &&
    mv ./web/.$APP_ENVIRONMENT.htaccess ./web/.htaccess || true
    fi
    rm -f ./web/.*.htaccess
    • Activates environment-specific .htaccess file
    • Removes other environment-specific .htaccess files
  9. Change to Ansible directory:

    cd ansible

Script

The main script performs the following steps:

  1. Configure playbook with host:

    sed -i "s/\$MITTWALD_HOST/${MITTWALD_HOST}/g" playbook-mittwald.yml
    • Replaces $MITTWALD_HOST placeholder with actual hostname
  2. Display playbook (for debugging):

    cat playbook-mittwald.yml
  3. Run Ansible playbook:

    ANSIBLE_REMOTE_TEMP=$MITTWALD_PATH ansible-playbook playbook-mittwald.yml \
    -i hosts-mittwald \
    --extra-vars "ansible_ssh_extra_args='-o StrictHostKeyChecking=no'" \
    --private-key="~/.ssh/id_rsa" \
    --extra-vars "ENVIRONMENT=$APP_ENVIRONMENT craft_src_path=$CI_PROJECT_DIR/ craft_www_path=$MITTWALD_PATH"

    Playbook Parameters:

    • playbook-mittwald.yml: The Ansible playbook for Mittwald deployment
    • -i hosts-mittwald: Inventory file with Mittwald hosts
    • ANSIBLE_REMOTE_TEMP: Sets temporary directory on remote host to deployment path
    • --extra-vars "ENVIRONMENT=$APP_ENVIRONMENT": Sets target environment
    • --extra-vars "craft_src_path=$CI_PROJECT_DIR/": Source path for deployment
    • --extra-vars "craft_www_path=$MITTWALD_PATH": Destination path on Mittwald server

Configuration Examples

Development Environment

variables:
ANSIBLE_MODULE_VERSION: v0.0.118
ANSIBLE_MODULE_PATH: welance/platform/infrastructure/modules/ansible.git
APP_ENVIRONMENT: 'develop'

# Required CI/CD variables:
# - MITTWALD_ID_DEVELOP: "12345"
# - MITTWALD_PATH_DEVELOP: "/www/htdocs/example"
# - MITTWALD_HOST_DEVELOP: "ssh.example.mittwald.de"

deploy-mittwald-dev:
extends: .ansible-craft-deploy
stage: deploy
only:
- develop

Production Environment

variables:
ANSIBLE_MODULE_VERSION: v0.0.118
ANSIBLE_MODULE_PATH: welance/platform/infrastructure/modules/ansible.git
APP_ENVIRONMENT: 'production'

# Required CI/CD variables:
# - MITTWALD_ID_PRODUCTION: "67890"
# - MITTWALD_PATH_PRODUCTION: "/www/htdocs/production"
# - MITTWALD_HOST_PRODUCTION: "ssh.production.mittwald.de"

deploy-mittwald-prod:
extends: .ansible-craft-deploy
stage: deploy
only:
- main
- tags

Complete Pipeline Example

variables:
ANSIBLE_MODULE_VERSION: v0.0.118
ANSIBLE_MODULE_PATH: welance/platform/infrastructure/modules/ansible.git

stages:
- build
- deploy

# Build job creates environment files
build:
stage: build
script:
- echo "DB_HOST=db.example.com" > .env.develop
- echo "DB_HOST=db.prod.example.com" > .env.production
- # Create environment-specific .htaccess files
- echo "RewriteEngine On" > ./web/.develop.htaccess
- echo "RewriteEngine On" > ./web/.production.htaccess
artifacts:
paths:
- .env.develop
- .env.production
- ./web/
- ./

.ansible-craft-deploy:
image: willhallonline/ansible:2.16.4-ubuntu-22.04
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
ROLE_DIR: ansible/roles/craft-rsync-deploy
before_script:
- eval "MITTWALD_ID=\${MITTWALD_ID_$(echo $APP_ENVIRONMENT | tr '[:lower:]' '[:upper:]')}"
- eval "MITTWALD_PATH=\${MITTWALD_PATH_$(echo $APP_ENVIRONMENT | tr '[:lower:]' '[:upper:]')}"
- eval "MITTWALD_HOST=\${MITTWALD_HOST_$(echo $APP_ENVIRONMENT | tr '[:lower:]' '[:upper:]')}"
- echo $MITTWALD_ID
- echo $MITTWALD_PATH
- echo $MITTWALD_HOST
- 'which ssh-agent || ( apt-get update -qy && apt-get install openssh-client -qqy )'
- apt-get update -qy && apt-get install rsync -qqy
- mkdir -p ~/.ssh/ && touch ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
- cat $ANSIBLE_SSH_ID_RSA > ~/.ssh/id_rsa
- echo "clone ansible module version "
- git clone --depth 1 --branch $ANSIBLE_MODULE_VERSION https://oauth2:[email protected]/$ANSIBLE_MODULE_PATH
- mv .env.$APP_ENVIRONMENT .env
- if [ -f ./web/.$APP_ENVIRONMENT.htaccess ]; then rm -f ./web/.htaccess && mv ./web/.$APP_ENVIRONMENT.htaccess ./web/.htaccess || true; fi
- rm -f ./web/.*.htaccess
- cd ansible
script:
- sed -i "s/\$MITTWALD_HOST/${MITTWALD_HOST}/g" playbook-mittwald.yml
- cat playbook-mittwald.yml
- ANSIBLE_REMOTE_TEMP=$MITTWALD_PATH ansible-playbook playbook-mittwald.yml -i hosts-mittwald --extra-vars "ansible_ssh_extra_args='-o StrictHostKeyChecking=no'" --private-key="~/.ssh/id_rsa" --extra-vars "ENVIRONMENT=$APP_ENVIRONMENT craft_src_path=$CI_PROJECT_DIR/ craft_www_path=$MITTWALD_PATH"
dependencies:
- build

deploy-mittwald-develop:
extends: .ansible-craft-deploy
variables:
APP_ENVIRONMENT: 'develop'
stage: deploy
only:
- develop

File Structure Requirements

The job expects the following file structure:

./
├── .env.develop
├── .env.staging
├── .env.production
└── web/
├── .develop.htaccess
├── .staging.htaccess
├── .production.htaccess
└── (other web files)

The job will:

  • Move .env.{environment} to .env
  • Activate the appropriate .htaccess file based on environment
  • Remove other environment-specific .htaccess files

Prerequisites

  • Ansible module repository: The Ansible module must exist at the specified GitLab path
  • SSH access: SSH keys must be configured and have access to Mittwald servers
  • GitLab access token: OAuth token with permissions to clone the Ansible module repository
  • Ansible playbook: The repository must contain a playbook-mittwald.yml file
  • Ansible hosts file: A hosts-mittwald inventory file must exist in the Ansible directory
  • Environment files: Environment-specific .env files must exist (.env.develop, .env.staging, .env.production)
  • .htaccess files (optional): Environment-specific .htaccess files can be provided
  • Mittwald account: Valid Mittwald hosting account with SSH access

Security Considerations

  • SSH Keys: Store SSH private key (ANSIBLE_SSH_ID_RSA) as a GitLab CI/CD masked variable
  • Access Token: Store the GitLab access token as a masked and protected variable
  • Mittwald Credentials: Store Mittwald-specific variables (ID, PATH, HOST) as masked variables
  • Key Permissions: SSH keys should have minimal required permissions
  • StrictHostKeyChecking: The job disables strict host key checking for convenience, but this should be reviewed for production use
  • Environment Files: Ensure .env files don't contain sensitive data or are properly secured

Notes

  • The job uses a shallow clone (--depth 1) to reduce clone time
  • Environment-specific variables are dynamically resolved based on APP_ENVIRONMENT
  • The .htaccess file handling ensures only the correct environment file is active
  • rsync is used for efficient file synchronization to Mittwald servers
  • The Ansible remote temp directory is set to the deployment path for efficiency
  • The job uses the Ansible image willhallonline/ansible:2.16.4-ubuntu-22.04 which includes Ansible 2.16.4
  • Mittwald-specific paths and hosts are configured per environment
  • The playbook receives both source and destination paths as extra variables