Preface

Previously, I introduced a VPS deployment method for Hexo blogs. However, although that method created a git user, it granted excessive permissions, which could potentially invite unnecessary security risks. Therefore, this article introduces a more secure VPS deployment approach. The main feature is creating a dedicated user blog with restricted permissions—limited to Git operations using SSH keys only, with no interactive login capability. Using this highly restricted user account to perform hook synchronization operations will be more secure and reliable. This serves as a supplement to the previous method.

I. Creating a Restricted User and Directories on the Server

The following commands are executed on a Debian 12 server (with sudo privileges).

  1. Install dependencies and create the site directory
sudo apt update
sudo apt install -y git
sudo mkdir -p /var/www/hexo
  1. Create a restricted user blog for Git operations only (disable password, use git-shell to restrict interaction)
sudo adduser --disabled-password --gecos "" --shell /usr/bin/git-shell blog
  1. Grant the blog user ownership of the site directory (or at least write permissions)
sudo chown -R blog:blog /var/www/hexo
sudo chmod -R u=rwX,g=rX,o=rX /var/www/hexo

If your web server (such as nginx) requires read permissions, the above permissions already cover this. If group collaboration is needed, you can change the group to www-data and grant group read permissions.

  1. Allow SSH key authentication only, disable interaction, disable port forwarding, etc. (enhanced security)

Create or edit /etc/ssh/sshd_config.d/blog.conf:

Match User blog
  PasswordAuthentication no
  AuthenticationMethods publickey
  X11Forwarding no
  AllowTcpForwarding no
  PermitTTY no

Then reload SSH:

sudo systemctl reload ssh

II. Configure SSH Public Key for the blog User

Generate a key pair on your development machine (local) if you don’t already have one, and add the public key to the blog user on the server.

On local machine (development machine):

# Skip if you already have one
ssh-keygen -t ed25519 -C "hexo-deploy" 
ssh-copy-id -i ~/.ssh/id_ed25519.pub blog@your.server.com

Or manually append the public key content to the server:

sudo -u blog mkdir -p ~blog/.ssh
sudo -u blog chmod 700 ~blog/.ssh
sudo -u blog bash -c 'cat >> ~blog/.ssh/authorized_keys'
sudo -u blog chmod 600 ~blog/.ssh/authorized_keys

III. Create a Bare Repository to Receive Pushes

  1. Create a bare repo as the blog user:
sudo -u blog mkdir -p ~blog/repos
sudo -u blog git init --bare ~blog/repos/hexo.git
  1. (Optional) Deploy only a specified branch (e.g., main), which will be used in the hook later.

IV. Write a post-receive Hook: Synchronize to /var/www/hexo Upon Receiving a Push

The hook runs with blog user identity, so blog must have write permissions to /var/www/hexo (already configured earlier).

Create the hook file and grant execution permissions:

sudo -u blog tee ~blog/repos/hexo.git/hooks/post-receive >/dev/null <<'HOOK'
#!/bin/sh
set -eu
TARGET="/var/www/hexo"
GIT_DIR="/home/blog/repos/hexo.git"
BRANCH="main"   # The branch name you want to deploy (main or master), remember to keep it consistent with your Hexo config
mkdir -p "$TARGET"
while read oldrev newrev ref
do
  if [ "$ref" = "refs/heads/$BRANCH" ]; then
    echo "Deploying $BRANCH to $TARGET ..."
    git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f "$BRANCH"
    git --work-tree="$TARGET" --git-dir="$GIT_DIR" clean -fdx
    chmod -R u=rwX,g=rX,o=rX "$TARGET"
    echo "Done."
  else
    echo "Push to $ref (ignored, only deploy $BRANCH)."
  fi
done
HOOK
sudo -u blog chmod +x ~blog/repos/hexo.git/hooks/post-receive

This solution does not leave a .git directory in /var/www/hexo (because it’s checked out from the bare repository in work-tree form), making it suitable for serving static files directly via nginx/Apache.


Next, you can configure nginx to point the site root to /var/www/hexo, and your Hexo blog will be publicly accessible. All subsequent steps remain exactly the same as before.

Plus Version of Deploying to VPS

Author

Shayne Wong

Publish Date

10 - 12 - 2025

License

Shayne Wong

Avatar
Shayne Wong

All time is no time when it is past.