4 minutes
Hugo Automated Deployments
Automating Hugo Website Deployments with GitLab CI/CD
As a DevOps engineer, I find it essential to automate as much of the development and deployment process as possible. This approach minimizes manual errors, saves time, and keeps workflows efficient. Today, I’m sharing how I automated the deployment of my Hugo-powered website using GitLab CI/CD, Linux, and Nginx, and a K3s cluster for running the GitLab Runner, with a pipeline that orchestrates the entire process from build to deployment.
The Setup
For my personal blog, I use Hugo CMS, a powerful static site generator that is lightning fast and fits perfectly with my philosophy of simplicity. To automate deployment, I utilize GitLab CI/CD to build and deploy the Hugo site, Nginx as the web server, . The pipeline ensures that every change I make to the content automatically gets deployed to my server, leveraging K3s for managing the GitLab Runner.
Here’s the GitLab CI/CD pipeline definition that I use to achieve this.
stages:
- build
- deploy
before_script:
- apk add --no-cache openssh-client rsync # Install SSH and rsync if using Alpine Linux Runner
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- if [ -n "$SSH_KNOWN_HOSTS" ]; then echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts; else ssh-keyscan -H "$SSH_HOST" >> ~/.ssh/known_hosts; fi
build:
stage: build
image: hugomods/hugo:latest
script:
- hugo -D -d public --config hugo.toml # Build the site into the public directory
artifacts:
paths:
- public # Save the public directory as an artifact
expire_in: 1 hour # Optional: Set an expiration time for the artifact
deploy:
stage: deploy
image: alpine:latest
script:
- apk add --no-cache openssh-client rsync
- rsync -avz --no-perms --no-owner --no-group --omit-dir-times --delete -e "ssh -o StrictHostKeyChecking=no" public/ "$SSH_USER@$SSH_HOST:$SSH_TARGET_DIR"
dependencies:
- build # Ensure deploy stage depends on build stage to retrieve artifacts
only:
- main
Pipeline Breakdown
-
- Stages
The pipeline is organized into two stages:
Build: Generates the static files using Hugo.
Deploy: Deploys the generated files to the server using rsync over SSH.
-
- Before Script Setup
The before_script section configures the SSH setup required for secure deployment. This step involves:
Installing SSH and rsync.
Setting up an SSH private key for authentication.
Configuring known_hosts for host verification.
These configurations ensure a secure and automated SSH connection to the target server.
-
- Build Stage
In the build stage, I’m using the Docker image hugomods/hugo:latest to run Hugo. The command hugo -D -d public –config hugo.toml is used to build the website, including any draft content (-D), and output the generated static files to the public directory. These files are then saved as artifacts for the next stage.
-
- Deploy Stage
The deployment stage involves using rsync to efficiently transfer the generated files to my server. The command rsync -avz –no-perms –no-owner –no-group –omit-dir-times –delete ensures:
File Synchronization: Updates the server with only the necessary changes.
Delete: Removes files on the server that no longer exist in the local build, keeping everything in sync.
I use an Alpine Docker image in this stage to keep things lightweight, and I ensure that the deploy stage depends on the build stage so that it has access to the generated files.
Key Considerations
Security: The use of SSH keys ensures a secure deployment. I’ve set up SSH key access to my server without passwords to facilitate automated deployments.
Efficiency: Using artifacts and rsync allows for efficient file transfer, minimizing bandwidth usage and speeding up deployment times.
Branch Control: The deployment only runs when changes are pushed to the main branch, allowing me to experiment on other branches without affecting the live site.
Lessons Learned
One of the challenges I faced initially was ensuring that the SSH key and known hosts were correctly configured within the GitLab CI environment. An improperly configured key can lead to frustrating failures in the deployment process. Making sure that SSH permissions and known hosts are correctly set up is crucial.
Another important aspect was optimizing the use of rsync. By using options like –no-perms, –no-owner, and –no-group, I was able to avoid permission issues on the target server, which can otherwise complicate deployment in containerized environments.
Conclusion
With this setup, I’m able to automatically deploy my Hugo-based website to my server whenever there’s an update. It takes advantage of modern CI/CD practices, using GitLab pipelines to ensure that every commit to main becomes a seamless, live deployment.
If you’re interested in setting up something similar, remember that it’s important to secure your keys, optimize the file transfer process, and ensure that permissions are handled properly to avoid headaches later on. Feel free to reach out if you have any questions or want to share your own experiences with similar setups!