Craft VM Deploy
The ansible-craft-vm-deploy job is used to deploy Craft CMS applications to virtual machines using Ansible. It deploys application files extracted from Docker containers to VM-based hosting.
Overview
This job:
- Uses Ansible to deploy Craft CMS applications to virtual machines
- Deploys application files extracted from Docker containers (from
.docker-build-vm-setupjob) - Clones a specific version of the Ansible module from GitLab
- Configures SSH access for remote server management
- Manages environment-specific configuration files
- Uses rsync for efficient file transfers
Variables
The following variables can be configured:
| Variable | Description | Default | Required |
|---|---|---|---|
ANSIBLE_MODULE_VERSION | Version/tag of the Ansible module to use | release/0.0.119 | No |
ANSIBLE_MODULE_PATH | GitLab path to the Ansible module repository | welance/platform/infrastructure/modules/ansible.git | No |
DOCKER_BUILD_IMAGE | Docker image for Docker-in-Docker service | 'docker:24.0.5' | No |
DOCKER_BUILD_SERVICE | Docker-in-Docker service version | '24.0.5-dind' | No |
DOCKER_COMPOSE_IMAGE | Docker Compose image | 'docker/compose:latest' | No |
DOCKER_COMPOSE_SERVICE | Docker Compose service | 'dind' | No |
ANSIBLE_DIR | Directory where Ansible code will be placed | infrastructure_code/configuration_management/ | No |
HOST | Target host address or hostname | '' | Yes |
VERSION | Application version/tag to deploy | '' | Yes |
VAR_FILES | Path to Ansible variable files | '' | No |
APP_ENVIRONMENT | Target environment | '' | Yes |
ANSIBLE_PORT | SSH port for Ansible connection | '' | Yes |
ANSIBLE_USER | SSH user for Ansible connection | '' | Yes |
Variable Details
- ANSIBLE_MODULE_VERSION: The Git tag or branch of the Ansible module repository to use (e.g.,
release/0.0.119) - ANSIBLE_MODULE_PATH: The GitLab project path to the Ansible module repository
- DOCKER_BUILD_IMAGE: Docker image version for Docker-in-Docker service (used if Docker operations are needed)
- DOCKER_BUILD_SERVICE: Docker-in-Docker service version
- DOCKER_COMPOSE_IMAGE: Docker Compose image (if compose operations are needed)
- DOCKER_COMPOSE_SERVICE: Docker Compose service
- ANSIBLE_DIR: Local directory where the Ansible module will be cloned and executed from
- HOST: The target server hostname or IP address where the deployment will occur
- VERSION: The application version/tag to deploy. Typically sourced from
.env.versionfile created by build jobs - VAR_FILES: Path to Ansible variable files (JSON or YAML) to pass additional configuration
- APP_ENVIRONMENT: Target environment for deployment. Options:
'develop'- Development environment'staging'- Staging environment'production'- Production environment
- ANSIBLE_PORT: SSH port number for connecting to the target host (e.g.,
22,2222) - ANSIBLE_USER: SSH username for authenticating to the target host
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")
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 target servers
Usage
Basic Usage
variables:
ANSIBLE_MODULE_VERSION: release/0.0.119
ANSIBLE_MODULE_PATH: welance/platform/infrastructure/modules/ansible.git
HOST: 'example.com'
VERSION: ''
VAR_FILES: ''
APP_ENVIRONMENT: 'develop'
ANSIBLE_PORT: '22'
ANSIBLE_USER: 'deploy'
.ansible-craft-vm-deploy:
image: willhallonline/ansible:2.16.4-ubuntu-22.04
services:
- docker:$DOCKER_BUILD_SERVICE
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
ANSIBLE_DIR: infrastructure_code/configuration_management/
HOST: ''
VERSION: ''
APP_ENVIRONMENT: ''
VAR_FILES: ''
ANSIBLE_PORT: ''
ANSIBLE_USER: ''
before_script:
- rm -rf dockerimage/app/helm-chart
- rm -rf dockerimage/app/infrastructure_code
- rm -rf dockerimage/app/.env*
- mv dockerimage/app .
- apt update && apt install -y rsync
- echo "clone ansible module version $ANSIBLE_MODULE_VERSION"
- git clone --depth 1 --branch $ANSIBLE_MODULE_VERSION https://oauth2:[email protected]/$ANSIBLE_MODULE_PATH
- mv ansible/* $ANSIBLE_DIR
script:
- cp .env.${APP_ENVIRONMENT} app/
- mv app/.env.${APP_ENVIRONMENT} app/.env
- ls -al ./app/
- source .env.version
- echo "Deploy version=$VERSION"
- mkdir -p ~/.ssh/ && touch ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
- cat $ANSIBLE_SSH_ID_RSA > ~/.ssh/id_rsa
- echo "ANSIBLE_HOST=${HOST}"
- echo "ANSIBLE_USER=${ANSIBLE_USER}"
- echo "ANSIBLE_PORT=${ANSIBLE_PORT}"
- sed -i "s/HOST_TO_BE_REPLACED/${HOST}/g" $ANSIBLE_DIR/hosts-vm
- sed -i "s/USER_TO_BE_REPLACED/${ANSIBLE_USER}/g" $ANSIBLE_DIR/hosts-vm
- sed -i "s/PORT_TO_BE_REPLACED/${ANSIBLE_PORT}/g" $ANSIBLE_DIR/hosts-vm
- cat $ANSIBLE_DIR/hosts-vm
- ansible-playbook $ANSIBLE_DIR/playbook-craft-vm.yml -b -i $ANSIBLE_DIR/hosts-vm --extra-vars "@${ANSIBLE_DIR}/${VAR_FILES}" --extra-vars "env=$APP_ENVIRONMENT" --extra-vars "tag=$VERSION" --extra-vars "ansible_ssh_extra_args='-o StrictHostKeyChecking=no'" --private-key="~/.ssh/id_rsa"
Job Details
Before Script
The before_script section performs the following setup:
-
Clean up extracted application files:
rm -rf dockerimage/app/helm-chart
rm -rf dockerimage/app/infrastructure_code
rm -rf dockerimage/app/.env*- Removes Helm charts (not needed for VM deployment)
- Removes infrastructure code
- Removes environment files (will be replaced with environment-specific ones)
-
Move application files:
mv dockerimage/app .- Moves the extracted application files from
dockerimage/appto the current directory - These files should have been extracted by a previous
.docker-build-vm-setupjob
- Moves the extracted application files from
-
Install rsync:
apt update && apt install -y rsync- Installs rsync for efficient file synchronization
-
Clone Ansible module:
git clone --depth 1 --branch $ANSIBLE_MODULE_VERSION https://oauth2:[email protected]/$ANSIBLE_MODULE_PATH
mv ansible/* $ANSIBLE_DIR- Clones only the specified version (shallow clone)
- Moves Ansible files to the configured directory
Script
The main script performs the following steps:
-
Copy environment-specific configuration:
cp .env.${APP_ENVIRONMENT} app/
mv app/.env.${APP_ENVIRONMENT} app/.env- Copies the environment-specific
.envfile (e.g.,.env.develop,.env.staging,.env.production) - Renames it to
.envin the application directory
- Copies the environment-specific
-
List application files (for debugging):
ls -al ./app/ -
Source version file:
source .env.version- Loads the
VERSIONvariable from the.env.versionfile created by build jobs
- Loads the
-
Display deployment version:
echo "Deploy version=$VERSION" -
Configure SSH key:
mkdir -p ~/.ssh/ && touch ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
cat $ANSIBLE_SSH_ID_RSA > ~/.ssh/id_rsa- Creates SSH directory and key file
- Sets proper permissions (600)
- Writes the SSH private key from GitLab variable
-
Display connection information:
echo "ANSIBLE_HOST=${HOST}"
echo "ANSIBLE_USER=${ANSIBLE_USER}"
echo "ANSIBLE_PORT=${ANSIBLE_PORT}" -
Configure Ansible hosts file:
sed -i "s/HOST_TO_BE_REPLACED/${HOST}/g" $ANSIBLE_DIR/hosts-vm
sed -i "s/USER_TO_BE_REPLACED/${ANSIBLE_USER}/g" $ANSIBLE_DIR/hosts-vm
sed -i "s/PORT_TO_BE_REPLACED/${ANSIBLE_PORT}/g" $ANSIBLE_DIR/hosts-vm
cat $ANSIBLE_DIR/hosts-vm- Replaces placeholders in the
hosts-vmfile with actual values - Displays the configured hosts file
- Replaces placeholders in the
-
Run Ansible playbook:
ansible-playbook $ANSIBLE_DIR/playbook-craft-vm.yml \
-b \
-i $ANSIBLE_DIR/hosts-vm \
--extra-vars "@${ANSIBLE_DIR}/${VAR_FILES}" \
--extra-vars "env=$APP_ENVIRONMENT" \
--extra-vars "tag=$VERSION" \
--extra-vars "ansible_ssh_extra_args='-o StrictHostKeyChecking=no'" \
--private-key="~/.ssh/id_rsa"Playbook Parameters:
playbook-craft-vm.yml: The Ansible playbook for Craft VM deployment-b: Use become (sudo) for privilege escalation-i $ANSIBLE_DIR/hosts-vm: Inventory file with target hosts--extra-vars "@${ANSIBLE_DIR}/${VAR_FILES}": Loads additional variables from file (if provided)--extra-vars "env=$APP_ENVIRONMENT": Sets the target environment--extra-vars "tag=$VERSION": Sets the application version/tag--private-key="~/.ssh/id_rsa": SSH private key for authentication
Configuration Examples
Development Environment
variables:
ANSIBLE_MODULE_VERSION: release/0.0.119
ANSIBLE_MODULE_PATH: welance/platform/infrastructure/modules/ansible.git
HOST: 'dev.example.com'
APP_ENVIRONMENT: 'develop'
ANSIBLE_PORT: '22'
ANSIBLE_USER: 'deploy'
deploy-craft-vm-dev:
extends: .ansible-craft-vm-deploy
stage: deploy
dependencies:
- build-vm-setup
only:
- develop
Production Environment with Custom Variables
variables:
ANSIBLE_MODULE_VERSION: release/0.0.119
ANSIBLE_MODULE_PATH: welance/platform/infrastructure/modules/ansible.git
HOST: 'prod.example.com'
APP_ENVIRONMENT: 'production'
ANSIBLE_PORT: '2222'
ANSIBLE_USER: 'deploy'
VAR_FILES: 'vars/production.yml'
deploy-craft-vm-prod:
extends: .ansible-craft-vm-deploy
stage: deploy
dependencies:
- build-vm-setup
only:
- main
- tags
Complete Pipeline Example
variables:
ANSIBLE_MODULE_VERSION: release/0.0.119
ANSIBLE_MODULE_PATH: welance/platform/infrastructure/modules/ansible.git
stages:
- build
- deploy
# Build job that extracts files from container
build-vm-setup:
extends: .docker-build-vm-setup
stage: build
artifacts:
paths:
- dockerimage/
- .env.version
- .env.develop
- .env.staging
- .env.production
.ansible-craft-vm-deploy:
image: willhallonline/ansible:2.16.4-ubuntu-22.04
services:
- docker:$DOCKER_BUILD_SERVICE
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
ANSIBLE_DIR: infrastructure_code/configuration_management/
HOST: ''
VERSION: ''
APP_ENVIRONMENT: ''
VAR_FILES: ''
ANSIBLE_PORT: ''
ANSIBLE_USER: ''
before_script:
- rm -rf dockerimage/app/helm-chart
- rm -rf dockerimage/app/infrastructure_code
- rm -rf dockerimage/app/.env*
- mv dockerimage/app .
- apt update && apt install -y rsync
- echo "clone ansible module version $ANSIBLE_MODULE_VERSION"
- git clone --depth 1 --branch $ANSIBLE_MODULE_VERSION https://oauth2:[email protected]/$ANSIBLE_MODULE_PATH
- mv ansible/* $ANSIBLE_DIR
script:
- cp .env.${APP_ENVIRONMENT} app/
- mv app/.env.${APP_ENVIRONMENT} app/.env
- ls -al ./app/
- source .env.version
- echo "Deploy version=$VERSION"
- mkdir -p ~/.ssh/ && touch ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
- cat $ANSIBLE_SSH_ID_RSA > ~/.ssh/id_rsa
- echo "ANSIBLE_HOST=${HOST}"
- echo "ANSIBLE_USER=${ANSIBLE_USER}"
- echo "ANSIBLE_PORT=${ANSIBLE_PORT}"
- sed -i "s/HOST_TO_BE_REPLACED/${HOST}/g" $ANSIBLE_DIR/hosts-vm
- sed -i "s/USER_TO_BE_REPLACED/${ANSIBLE_USER}/g" $ANSIBLE_DIR/hosts-vm
- sed -i "s/PORT_TO_BE_REPLACED/${ANSIBLE_PORT}/g" $ANSIBLE_DIR/hosts-vm
- cat $ANSIBLE_DIR/hosts-vm
- ansible-playbook $ANSIBLE_DIR/playbook-craft-vm.yml -b -i $ANSIBLE_DIR/hosts-vm --extra-vars "@${ANSIBLE_DIR}/${VAR_FILES}" --extra-vars "env=$APP_ENVIRONMENT" --extra-vars "tag=$VERSION" --extra-vars "ansible_ssh_extra_args='-o StrictHostKeyChecking=no'" --private-key="~/.ssh/id_rsa"
deploy-craft-vm-develop:
extends: .ansible-craft-vm-deploy
variables:
HOST: 'dev.example.com'
APP_ENVIRONMENT: 'develop'
ANSIBLE_PORT: '22'
ANSIBLE_USER: 'deploy'
stage: deploy
dependencies:
- build-vm-setup
only:
- develop
Prerequisites
- Docker build with VM setup: A previous job (
.docker-build-vm-setup) must extract application files todockerimage/app/ - Environment files: Environment-specific
.envfiles (.env.develop,.env.staging,.env.production) must exist - Version file: A
.env.versionfile must be created by a previous build job - Ansible module repository: The Ansible module must exist at the specified GitLab path
- SSH access: SSH keys must be configured and have access to target servers
- GitLab access token: OAuth token with permissions to clone the Ansible module repository
- Ansible playbook: The repository must contain a
playbook-craft-vm.ymlfile - Ansible hosts template: A
hosts-vmfile with placeholders (HOST_TO_BE_REPLACED,USER_TO_BE_REPLACED,PORT_TO_BE_REPLACED) must exist - Target servers: Servers must be accessible via SSH and have rsync available
- Sudo access: The Ansible user must have sudo privileges on target servers (uses
-bflag)
Differences from Docker Deploy
| Feature | Craft Docker Deploy | Craft VM Deploy |
|---|---|---|
| Target | Docker containers | Virtual machines |
| Application Source | Docker images from registry | Files extracted from containers |
| Deployment Method | Docker pull and run | File synchronization (rsync) |
| Prerequisites | Docker build job | Docker build + VM setup job |
| Environment Files | Not copied | Copied and renamed to .env |
| Privilege Escalation | Not required | Uses -b (become/sudo) |
| Cleanup | N/A | Removes helm-chart, infrastructure_code, .env* files |
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
- Key Permissions: SSH keys should have minimal required permissions
- Sudo Access: Ensure the Ansible user has only necessary sudo permissions
- StrictHostKeyChecking: The job disables strict host key checking for convenience, but this should be reviewed for production use
- Environment Files: Ensure environment files don't contain sensitive data or are properly secured
Notes
- The job expects application files to be extracted by a previous
.docker-build-vm-setupjob - Files are cleaned up before deployment (removes helm-chart, infrastructure_code, .env*)
- Environment-specific
.envfiles are copied and renamed to.envin the application directory - The job uses rsync for efficient file synchronization to target servers
- The Ansible playbook runs with privilege escalation (
-bflag) for system-level operations - The
hosts-vmfile is dynamically configured with target host information - Docker-in-Docker service is available but primarily used if Docker operations are needed during deployment
- The Ansible image
willhallonline/ansible:2.16.4-ubuntu-22.04includes Ansible 2.16.4