I've set up a GitHub repository which contains an example of how automatically deploy a Go web application to NearlyFreeSpeech.net. It shows continuous deployment for a custom web server that automatically updates whenever you push changes to your repository.
NearlyFreeSpeech.net (NFSN) is a usage-based billing web hosting service that supports both static sites and custom server applications. This repository shows step-by-step how to:
Add these required secrets to your GitHub repository:
NFSN_HOSTNAME
: Your SSH/SFTP hostname from NFSN site informationNFSN_USERNAME
: Your NFSN site usernameNFSN_PASSWORD
: Your NFSN account passwordNFSN_SHORTNAME
: Your site's short nameBEARER_TOKEN
: A secret token for the shutdown endpoint (you choose this value)The SSH/SFTP information can be found on the site information page for your site.
You application needs two key features:
GOOS
and GOARCH
environment variables.GOOS=freebsd GOARCH=amd64 go build ./...
e.POST("/shutdown", func(c echo.Context) error {
token := c.Request().Header.Get("Authorization")
expectedToken := "Bearer " + os.Getenv("BEARER_TOKEN")
if token != expectedToken {
return echo.NewHTTPError(http.StatusUnauthorized, "Invalid token")
}
// allow some time to send a response before app exits
go func() {
log.Println("Shutdown triggered via /shutdown: 5s...")
time.Sleep(time.Second * time.Duration(5))
os.Exit(42)
}()
return c.String(http.StatusOK, "shutdown in 5s\n")
})
Create run.sh
to set environment variables and start your server:
Your application will probably want some environment variables set, including our BEARER_TOKEN
to protect the shutdown endpoint.
#!/bin/sh
set -eou pipefail
export BEARER_TOKEN="$$BEARER_TOKEN"
/home/protected/app
NearlyFreeSpeech.net doesn't have any direct integration with Github; deployment is achieved the old-fashioned way, by copying your files to the server.
Your GitHub action will:
protected/
protected/run.sh
rsync
(or scp
, or SFTP, or ...)sshpass -p "${{ secrets.NFSN_PASSWORD }}" \
rsync -avz \
-e "ssh -o StrictHostKeyChecking=no" \
--delete protected/ "${{ secrets.NFSN_USERNAME }}"@"${{ secrets.NFSN_HOSTNAME }}":/home/protected/
This command makes use of some of the GitHub secrets previously described, so this information doesn't appear in the GitHub actions logs.
It moves the contents of the protected/
directory to /home/protected
in the NearlyFreeSpeech.net site.
It uses sshpass
fill your password in rsync
's SSH authentication prompt.
Since GitHub actions are ephemeral, each time it runs, it will appear that this is the first time you've used SSH to connect to NearlyFreeSpeech.net: -e "ssh -o StrictHostKeyChecking=no"
tells SSH to skip the prompt for you to okay the remote host key.
--delete
causes rsync
to delete anything in /home/protected
that is not in protected/
, meaning you can remove or rename your generated files, and the corresponding change will be made on the NearlyFreeSpeech.net site.
If we were just deploying static files, the next time we navigated to the site, NFSN would just serve us the updated files. For our app, though, NFSN will still be running the old binary.
Fortunately, NFSN will try to restart our daemon command if it has exited. So, the last thing our GitHub action does is tell the currently-running app to shut down:
curl -X POST -H "Authorization: Bearer ${{ secrets.BEARER_TOKEN }}" "https://${{ secrets.NFSN_SHORTNAME }}.nfshost.com/shutdown"
You'll see in the site event log that the daemon shuts down, and a few seconds later it will automatically be restarted, which launches the new binary.
In this example, our app listens on port 8080, so we need to forward HTTP accesses to that port.
Site Information > Add a Proxy
2: Configure the Application to Run
We need to tell NearlyFreeSpeech.net to run the app we've uploaded.
Site Information > Add Daemon
/home/protected/run.sh