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
.htaccessfiles - Supports environment-specific Mittwald configuration
Variables
The following variables can be configured:
| Variable | Description | Default | Required |
|---|---|---|---|
ANSIBLE_MODULE_VERSION | Version/tag of the Ansible module to use | v0.0.118 | No |
ANSIBLE_MODULE_PATH | GitLab path to the Ansible module repository | welance/platform/infrastructure/modules/ansible.git | No |
ROLE_DIR | Directory where Ansible role is located | ansible/roles/craft-rsync-deploy | No |
APP_ENVIRONMENT | Target 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 totcp://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 repositoryANSIBLE_SSH_ID_RSA: SSH private key content for authenticating to Mittwald serversMITTWALD_ID_DEVELOP,MITTWALD_ID_STAGING,MITTWALD_ID_PRODUCTION: Mittwald account IDs per environmentMITTWALD_PATH_DEVELOP,MITTWALD_PATH_STAGING,MITTWALD_PATH_PRODUCTION: Deployment paths per environmentMITTWALD_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:
-
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.,
develop→DEVELOP) - Example: For
APP_ENVIRONMENT=develop, resolvesMITTWALD_ID_DEVELOP
-
Display resolved variables (for debugging):
echo $MITTWALD_ID
echo $MITTWALD_PATH
echo $MITTWALD_HOST -
Install SSH client (if not available):
which ssh-agent || ( apt-get update -qy && apt-get install openssh-client -qqy ) -
Install rsync:
apt-get update -qy && apt-get install rsync -qqy -
Configure SSH key:
mkdir -p ~/.ssh/ && touch ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
cat $ANSIBLE_SSH_ID_RSA > ~/.ssh/id_rsa -
Clone Ansible module:
git clone --depth 1 --branch $ANSIBLE_MODULE_VERSION https://oauth2:[email protected]/$ANSIBLE_MODULE_PATH -
Prepare environment file:
mv .env.$APP_ENVIRONMENT .env- Moves environment-specific
.envfile to.env
- Moves environment-specific
-
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
.htaccessfile - Removes other environment-specific
.htaccessfiles
- Activates environment-specific
-
Change to Ansible directory:
cd ansible
Script
The main script performs the following steps:
-
Configure playbook with host:
sed -i "s/\$MITTWALD_HOST/${MITTWALD_HOST}/g" playbook-mittwald.yml- Replaces
$MITTWALD_HOSTplaceholder with actual hostname
- Replaces
-
Display playbook (for debugging):
cat playbook-mittwald.yml -
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 hostsANSIBLE_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
.htaccessfile based on environment - Remove other environment-specific
.htaccessfiles
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.ymlfile - Ansible hosts file: A
hosts-mittwaldinventory file must exist in the Ansible directory - Environment files: Environment-specific
.envfiles must exist (.env.develop,.env.staging,.env.production) - .htaccess files (optional): Environment-specific
.htaccessfiles 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
.envfiles 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
.htaccessfile 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.04which 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