What you'll need to follow this guide:

  • GitLab Repo with pipelines enabled
  • Superuser account on server (this guide is written for Ubuntu but other distributions should still be very similar)
  • SSH enabled on destination server
  • A running webserver (we'll assume NGINX is running here)
  • A Hexo website you wish to deploy
  • (optional) an SSL Certificate for use with your site or LetsEncrypt setup knowledge


A few weeks back, I was looking for an easy-to-use framework for blogging. Hexo was highly recommended and I thought I'd give it a try. What I think of Hexo will be covered in a separate post.

In this guide, I will be using GitLab Pipelines to deploy a Hexo site to a server that I control.


  • No-fuss automated deployment – just update your master branch and let pipelines handle the rest


  • GitLab Pipelines - Free
  • Server hosting costs apply

Implementation Guide:

I'm going to assume you have a Hexo site by now since that is not covered in this guide.

1. Create a user on your server for deployment.

We're going to set up some pre-requisites on the server-side, starting with setting up a deployment user account aptly named ‘deploy’: sudo adduser --gecos --disabled-password deploy (Optional) you can skip the password part, we'll be using SSH trust with a keypair for this guide

This user is what we will use to connect to your server hosting Hexo.

2. Change to deploy user home directory:

sudo su - deploy

3. Set up a keypair for the deploy user:

ssh-keygen -t rsa -b 4096 Call this key what you want, we will use the ssh-keygen defaults for this guide in the default locations

This should net you two files: ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub

The private key id_rsa should be kept secret, for most part, and the public key id_rsa.pub will be added to your server to allow SSH trust in the next step.

4. Modify your server's SSH authorized_keys:

  • create the authorized_keys file in the deploy user's ‘.ssh/’ directory if it doesn't exist: touch ~/.ssh/authorized_keys
  • put the public key created in step 3 into the authorized_keys file: echo ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
Now anything using the id_rsa.pub private key will be able to connect to your server via SSH (rsync uses SSH) as the deploy user.

5. Add the deploy user private key to your repo:

Save the private key of the deploy user from ~/.ssh/id_rsa into the root directory of your repository, we will call this key hexo_deploy.pem for this guide.

This key will be checked aganinst the public key that was added to authorized_keys in the previous step.

As mentioned earlier, the private key should be kept secret for the most part. Don't check this into a publically-visible repository.

6. Create an ssh_config file that will later be used by the pipelines docker container:

host $your_hostname.fqdn
    User deploy
    UserKnownHostsFile /dev/null
    StrictHostKeyChecking no
    UpdateHostKeys no
    IdentityFile ~/.ssh/hexo_deploy.pem
This config is a helper to ensure that the right key is used when calling the specified host

You'll want to add your proper hostname here of course.

7. Install the hexo rysnc type deployer in your project directory:

npm install hexo-deployer-rsync --save

This NPM package will allow rsync operations to be performed as part of your Hexo installation.

8. Edit the Hexo project _config.yml file and enable rsync type deployment:

# Deployment
## Docs: https://hexo.io/docs/deployment.html
# Deployment Settings
  type: rsync
  host: $your_hostname.fqdn
  user: deploy
  root: /var/www/html
  port: 22 
  delete: true
  args: -azvr
  verbose: true
  ignore_errors: false
Now you should be able to use rsync to deploy to your server from your Hexo installation.

As with other steps, do ensure you set the correct hostname for your server. Additionally set the proper root directory of where your static files live and will be served by NGINX.

9. Change ownership of your webroot to deployment user:

Lastly on the server, change ownership of your webroot directory, let's assume that is /var/www/html to match with the above Hexo deployment config.

  • exit out of the deploy user’ shell with exit (as your deploy user shouldn't have sudo)
  • change permissions on your webroot: sudo chown -R deploy.deploy /var/www/html

Your nginx.conf may look something similar to this:

user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
	worker_connections 768;
	# multi_accept on;

http {

	# Basic Settings

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	# server_tokens off;

	# server_names_hash_bucket_size 64;
	# server_name_in_redirect off;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	# SSL Settings

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	# Logging Settings

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	# Gzip Settings

	gzip on;

	gzip_vary on;
	gzip_proxied any;
	gzip_comp_level 6;
	gzip_buffers 16 8k;
	gzip_http_version 1.1;
	gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  listen 80;
  server_name mysite.com;
  rewrite ^ https://$http_host$request_uri? permanent; #optional, this is to redirect HTTP to HTTPS

  # IF you want to serve via HTTP, you can comment out the next 5 lines below:
  #root /var/www/html;
  #index index.html;
  #location / {
  #  try_files $uri $uri/ =404;

server # IF you don't want to serve HTTPS, comment out this entire stanza STARTing from this line;

  listen 443;
  server_name mysite.com;
  ssl on;
  ssl_certificate         /etc/letsencrypt/live/mysite.com/fullchain.pem;
  ssl_certificate_key     /etc/letsencrypt/live/mysite.com/privkey.pem;
  underscores_in_headers on;
  root /var/www/html;
  index index.html;

  location / {
    try_files $uri $uri/ =404;
} # END commenting out the HTTPS listener on 443 at this line.


This is a basic NGINX config that will redirect non-http requests to an https listener on port 443. This example also uses LetsEncrypt for SSL Certificates but you are certainly welcome to use your own.

You will want to ensure that server_name matches your website domain.

10. Set up GitLab Pipeline:

To start with GitLab Pipelines, create a .gitlab-ci.yml file in your repository with your preferred editor:

image: crungruang/hexo-deploy:latest

    - node_modules/

  - mkdir -p ~/.ssh
  - cp hexo_deploy.pem ~/.ssh/
  - chmod 600 ~/.ssh/hexo_deploy.pem
  - cp ssh_config ~/.ssh/config
  - npm install hexo-cli -g
  - test -e package.json && npm install
  - hexo generate

    - hexo deploy --generate
      - public
    - master
_This gilab-ci.yml example uses a basic npm 10x image on DockerHub with Hexo and rsync installed but you can choose your own image, of course.

By now your repository root should look something like this:

11. Commit your changes to the master branch git push origin master and it should trigger the following sequence of events:

  a. check out the specified docker image `crungruang/hexo-deploy:latest`

  b. add your private key and ssh_config to docker container

  c. install hexo-cli 

  d. run some basic tests and install necessary npm modules

  e. generate static files for your site

  f. deploy your site according to the Hexo `_config.yml` _deploy_ stanza


If you were able to follow along and make the necessary modifications according to your situation, you should have a working auto CI/CD in Gitlab with your Hexo installation.


  • Typically one does not commit private keys into a repo, it isn't the best of practices outside of personal blog use but this is just one of many ways of working with your blog
  • There are certainly other deploy methods that Hexo provides, you should see what is best for you – many choose (wisely) something like S3 – I just happen to have a spare server or two or three to play with..