- Shell 94.7%
- Go Template 5.3%
| scripts | ||
| .gitignore | ||
| action.yml | ||
| README.md | ||
Deploy a Static Site to Tailscale Node
A composite Forgejo CI action that joins a Headscale tailnet and
rsyncs a built site to a remote node over Tailscale SSH.
It is designed for static-site pipelines: build your site in an earlier step, then
hand the output directory to this action to publish it to /var/www/<site> on
the target node.
I have built this primarily for myself, to deploy my blog and other static sites.
How it works
- Brings up Tailscale in userspace-networking mode with a SOCKS5 proxy on
localhost:1055, authenticating to your Headscale login server with a pre-auth key. - Installs
rsync,ssh, andncif they are missing (Debian/Ubuntu runners). - Writes an
~/.ssh/configthat reaches the deploy host through the Tailscale SOCKS5 proxy viaProxyCommand. - Ensures
/var/www/<site>exists on the remote, then mirrors the source directory into<deploy-root><site>/withrsync -azc --delete.
Note:
--deletemeans the remote target is mirrored exactly — files not present in the source directory are removed from the target.
Inputs
| Input | Required | Default | Description |
|---|---|---|---|
site |
yes | — | Site name; target becomes /var/www/<site> |
ts-authkey |
yes | — | Headscale pre-auth key |
source-dir |
no | . |
Built site directory, relative to the workspace |
deploy-root |
no | /var/www/ |
Target root directory on the remote node |
deploy-host |
no | 100.64.0.4 |
Target Tailscale IP address (100.64.0.x) |
deploy-user |
no | deploy |
Target SSH username |
ts-login-server |
no | https://hs.thms.uk |
Headscale login server |
Usage
Create an ephemeral, reusable preauth key on your headscale server, making sure you give it whatever tag your ACLs need, e.g. tag:ci:
headscale preauthkeys create --ephemeral --reusable --tags tag:ci -e 876000h
Supply the key as Action secret with name TS_AUTHKEY to your Forgejo repository.
Then create a deploy workflow in your repository:
jobs:
deploy:
runs-on: ubuntu-latest # A Debian/Ubuntu-based runner
steps:
- uses: https://github.com/actions/checkout@v4
# ... build your site into ./public ...
- name: Deploy
uses: https://code.thms.uk/michael/tailscale-deploy@<version>
with:
site: example.com # Site name; target becomes `/var/www/<site>`
source-dir: public # Build step's output directory
deploy-host: 100.x.y.z # Target Tailscale IP address
deploy-user: www-data # the user you use to SSH to the node
ts-authkey: ${{ secrets.TS_AUTHKEY }} # Headscale pre-auth key
ts-login-server: https://headscale.example.org # Headscale login server
Requirements
- A Debian/Ubuntu-based runner (the action uses
apt-getto install any missing tools). - A reachable Headscale server and a valid pre-auth key (store it as a CI secret).
- The deploy node advertised on the tailnet, running an SSH server that accepts the
deploy-user, with write access to<deploy-root>. - The runner's tailnet identity authorized for Tailscale SSH to the target node.
Security notes
StrictHostKeyCheckingis set toaccept-new, so the host key is trusted on first connection. Connections route entirely over the tailnet via the SOCKS5 proxy.- Always pass
ts-authkeyfrom a secret — never inline it in the workflow file.
Questions / Comments / Bugs?
Reach me on Mastodon, or find more details on my personal website.