Hugo: Deploy to server

First, I want to say, that there are a lot of ways to deploy your hugo site to the server. I will show you my way, which I use for my blog. I’m not pretending that it’s the best way, but it works for me.

What is hugo ?

Hugo is a static site generator that allows you to create a website with little to no code. It is one of the most popular static site generators and is known for its speed, ease of use, and flexibility. Also, it’s a lovely open-source project, which you can find on GitHub.

How does adding a new post look like?

It’s very simple. You just need to run hugo new posts/your-post-name.md and it will create a new post for you. After that, you can open it in your favorite editor and start writing using Markdown. And when you finish, you can run hugo to build static content, that’s ready to use with some HTTP-server.

Deploy

I store my blog on GitHub, and you might think about GitHub Actions, when somebody says about deployment. And you will be right, but I decided to do it via GitHub webhooks. I have a small server, which I use for my projects which sometimes also uses webhooks. So, I decided to use it for my blog too.

The main idea was to create a webhook service, that received requests from the source and decided what to do next.

How it works:

  1. I do some changes in my blog and push them to GitHub.
  2. GitHub sends a request to my webhook service.
  3. Webhook service receives the request and pull changes from GitHub and generates static files.

DevOps part ;)

There is nginx on my server, which I used as an HTTP-server for my projects.

I created a subdomain for webhook service and added a new nginx config, that approximately looks like this:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate         /etc/ssl/cert.pem;
    ssl_certificate_key     /etc/ssl/key.pem;

    server_name {your_server_name};

    root {your_root_path};

    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    location /blog/webhook {
        include uwsgi_params;
        uwsgi_pass unix:{your_uwsgi_socket_path};
    }
}

And you can see that I use uwsgi to run my webhook service. That’s because I wrote it on Python ❤️. At this moment I use Flask, but I maybe rewrite it on FastAPI. Time will tell.

If you worked with Flask/Django/FastAPI/etc, you definitely know that you need to run it with some WSGI server. I use uwsgi, but you can use gunicorn or something else.

So… There is a small part of my uwsgi config:

[uwsgi]
module = webhook:app
master = true
processes = 2
socket = {your_uwsgi_socket_path}
chmod-socket = 664
vacuum = true
die-on-term = true

And the last part is the webhook service itself. I use Flask, so here you can see an example of my webhook service, generated by GitHub Copilot:

from flask import Flask, request
from subprocess import run

app = Flask(__name__)

@app.route('/blog/webhook', methods=['POST'])
def webhook():
    if request.method == 'POST':
        run(['git', 'pull', 'origin', 'master'])
        run(['hugo'])
        return 'OK'
    else:
        return 'OK'

if __name__ == '__main__':
    app.run()

Also, I use systemd to run my webhook service. It’s very simple, you just need to create a new service file in /lib/systemd/system/ and add something like this:

[Unit]
  Description=Webhook uwsgi runner

[Service]
  WorkingDirectory={your_working_directory}
  ExecStart=/usr/local/bin/uwsgi --ini uwsgi.ini --need-app
  Type=idle
  KillMode=process

  SyslogIdentifier=webhook
  SyslogFacility=daemon

  Restart=on-failure
  User=www-data
  Group=www-data

[Install]
  WantedBy=multiuser.target

And that’s all. Now you can run service webhook start and your webhook service will start.

Conclusion

I hope you enjoyed this article, and it will help you to deploy your hugo site to the server. If you have any questions, feel free to ask me at [email protected].