Ecmel Berk Canlıer - 9 May 2020

nginxpages: GitHub Pages-like static sites, but self-hosted

Do you want to have the ease of git pushing a repo, and having your site updated, on your own server, without being locked into GitHub, GitLab, or anything else? If so, you might want to check out my new thing, nginxpages.

nginxpages is essentially just Nginx and Git taped together and presented in a easy to install Docker container (It’s actually multiple containers, managed via compose)

Now, the concept itself will still work without Docker, as the configuration isn’t any hard, and could probably be copied and edited to work without it very easily.

Requirements

Container Setup

git clone https://git.sr.ht/~admicos/nginxpages
cd nginxpages

Before doing anything else, you might want to check docker-compose.yml to get a feel for the default configuration. It’s very bare bones, as you’re expected to wire it up to your own reverse proxy. (or plug straight into the internet)

This following code block is my configuration for nginx-proxy, which I use as my reverse proxy. If you use something else, (e.g Traefik, or hand crafted configs) this can still be useful as a reference.

For wiring up to my reverse proxy, I created a docker-compose.override.yml file, and put the following in it:

# nginx-proxy only!

version: "3"

services:
  nginx:
    container_name: pages-nginx
    environment:
      VIRTUAL_HOST: "ecmelberk.com,*.ecmelberk.com"
      VIRTUAL_PORT: "8080"
      CERT_NAME: "wildcard"
    networks:
      - webnet
  git:
    container_name: pages-git

networks:
  webnet:
    external: true

This should be it for configuring the containers themselves. Now, we just need to expose our Git repo to the internet.

Git SSH Setup

Now, a word of warning: THE DEFAULT git CONTAINER’S CONFIGURATION IS INSECURE! DO NOT PUT IT ON THE INTERNET WITHOUT A GATEWAY IN FRONT! READ THE REST OF THIS TO LEARN MY SETUP!

Before starting up your containers, please firewall port 2222 (git container’s port) to only allow connections from localhost, otherwise you’re giving the entire internet write access to your sites.

Firstly, create an user in your host:

# This is for Alpine Linux. Check your distribution's documentation on how to
# create an user.

sudo adduser -h /home/pages -D pages

The only purpose of this user is to be the gateway for your SSH connections.

Now, we’ll need to make any connections to this user connect to our git container.

Before doing any of the following, please read the wrap.sh script, and modify to your needs, if any.

chmod +x nginxpages/wrap.sh
echo "/home/you/nginxpages/wrap.sh" | sudo tee /etc/shells

# Change user's shell to wrap.sh
# Most distributions have a command like `chsh`
# You might want to use that instead
sudo nvim /etc/passwd

After changing the user’s shell. We need to do one last thing:

Creating new SSH keys

While this step is optional, it’s recommended unless you plan to directly push onto your server, and not via some kind of CI/CD system.

ssh-keygen -t rsa -b 4096 -C "pages@example.com"
# Keys will be referred to as `pages_rsa` and `pages_rsa.pub` from now on.

After creating your keys, authorize the public key to connect to the new user:

sudo mkdir -p /home/pages/.ssh
cat pages_rsa.pub | sudo tee /home/pages/.ssh/authorized_keys

Testing

That should be it. Now, start the containers, and try logging into your test user:

server$ cd nginxpages

# Start containers and show logs while starting up.
# Ctrl+C to exit, but it will not shut the containers down
server$ docker-compose up -d && docker-compose logs -f
...

client$ ssh -i pages_rsa pages@example.com
This is just a regular git-shell, with two extra commands:
  - new-site <domain>
  - remove-site <domain>
git> exit

If you see any output like the above, then congratulations! Everything should be set up.

And with your web browser, go to a domain you configured to point towards nginxpages. If you see a centered 404 (which should respect prefers-color-scheme: dark), congratulations, the web side is working as well!

Usage

Now that your server is set up, here is how you can use it:

Creating a new repository

ssh -i pages_rsa pages@example.com new-site example.com

This will make the site return a 403 if there isn’t any index.

Removing a repository

THIS COMMAND WILL NOT ASK CONFIRMATION

ssh -i pages_rsa pages@example.com remove-site example.com

Cloning your repository

To use Git with your custom SSH key, you can use ~/.ssh/config on your client to tell SSH to try the second key as well as your primary one.

$ cat ~/.ssh/config

IdentityFile ~/.ssh/id_rsa
IdentityFile ~/.ssh/pages_rsa

After setting that up, you can clone your repository:

git clone pages@example.com:sites/example.com

# Or if your SSH is running on a non-standard port:
git clone ssh://pages@example.com:2222/~/sites/example.com

CI

If you have a CI/CD system, and you want to use a static site generator or something similar, take a look at how I do it on my own site:

Finale

I hope nginxpages will be helpful to you.

For patches sent via email, please make sure you indicate that it’s for nginxpages by running the following on your clone of the repo:

git config format.subjectprefix "PATCH nginxpages"