Every GitHub deploy on FPS.ms runs a deploy script: a short Bash script that fetches your commit and installs your packages before the server restarts. You can add your own build steps to it on the Script tab, for example npm run build or a database migration. To deploy only code that passed its tests, let GitHub Actions push to the branch your server deploys, or call the CI webhook URL from your workflow. This guide assumes you already deploy from GitHub.
What the default script does
Every runtime starts with a script that works for most projects. It fetches the commit you pushed, then runs the install or build step below. When it finishes, the server restarts and starts your app.
| Runtime | What the default script runs |
|---|---|
| Python | pip install from requirements.txt |
| Node.js | npm ci when there is a package-lock.json, otherwise npm install |
| Bun | bun install |
| .NET | dotnet publish on your .csproj |
| Dart | dart pub get, then dart compile exe |
| Go | go build |
| Java | mvn package with a pom.xml, otherwise a Gradle build |
| Kotlin | A Gradle build, or kotlinc when there is no Gradle project |
| Rust | cargo install from your crate |
| Haskell | cabal build, or stack build when there is no .cabal file |
| Lua | luarocks install for your .rockspec and extra rocks |
| Odin | odin build |
Add your own build steps
- Click Manage on your server, then More and Deployments.
- Click Script. The editor shows your server's deploy script.
- Add your commands at the end. Keep the lines that fetch the commit at the top.
- Click Save. The new script is used from the next deploy.
For example, a Node.js app that compiles TypeScript ends its script like this:
# Install dependencies.
if [ -f package-lock.json ]; then
npm ci --no-audit --no-fund
elif [ -f package.json ]; then
npm install --no-audit --no-fund
fi
# Compile TypeScript into dist/
npm run build
And a Django app can run its migrations on every deploy:
python manage.py migrate
python manage.py collectstatic --noinput
The script stops at the first command that fails, and the deploy is marked as failed. The build log shows which line it was.
Back to the default
Click Reset to egg default to throw away your changes and use the runtime's default script again.
Variables you can use in the script
| Variable | What it holds |
|---|---|
$RELEASE_DIR |
The folder the deploy builds in: your server's main folder |
$COMMIT_SHA |
The commit being deployed |
$BRANCH |
The branch that was pushed |
$DEPLOYMENT_ID |
A unique ID for this deploy |
Your environment variables are available in the script too, for example an API key a build step needs.
Deploy only code that passed its tests
A push to the connected branch deploys right away. To deploy only tested code, let your tests decide what reaches that branch:
- Connect your server to a branch that only your workflow writes to, for example
production. - Run your tests on every push to
mainin GitHub Actions. - When they pass, the workflow pushes the commit to
production, and FPS.ms deploys it.
name: Test and deploy
on:
push:
branches: [main]
permissions:
contents: write
jobs:
test-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: pip install -r requirements.txt
- run: python -m pytest
- name: Deploy tested code
run: git push origin HEAD:production
If the push does not start a deploy, add the webhook call from the next section after the git push step.
Start a deploy from CI with the webhook URL
Every connected server also has a CI webhook URL. Calling it starts a deploy of the newest commit on the connected branch, or of the commit you name. Use it when a deploy should wait for something else, like a release you publish by hand.
- On the Deployments tab, click Settings.
- Under CI webhook URL, copy the Webhook URL.
- Click Rotate deploy token and copy the token. It is only shown once.
- In GitHub, open your repository's Settings, then Secrets and variables and Actions. Add two secrets:
FPS_DEPLOY_URLwith the URL andFPS_DEPLOY_TOKENwith the token. - Add a step that calls the URL:
- name: Deploy to FPS.ms
run: |
curl --fail -X POST "$FPS_DEPLOY_URL" \
-H "Authorization: Bearer $FPS_DEPLOY_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"commit_sha\": \"$GITHUB_SHA\"}"
env:
FPS_DEPLOY_URL: ${{ secrets.FPS_DEPLOY_URL }}
FPS_DEPLOY_TOKEN: ${{ secrets.FPS_DEPLOY_TOKEN }}
Leave out the -d line to deploy the newest commit on the branch.
Auto-deploy must be on
The webhook URL only works while Auto-deploy is on, so pushes to the connected branch deploy too. If you want CI to be the only way in, connect a branch nobody pushes to by hand.
If the call fails, the answer says why:
| Answer | Meaning |
|---|---|
401 invalid_token or missing_token |
The token is wrong, revoked or not sent |
409 auto_deploy_disabled |
Turn Auto-deploy on |
422 server_not_bound |
No repository is connected to the server |
429 deploy_rate_limited |
Too many deploys in a short time; wait and try again |
To stop old tokens from working, click Rotate deploy token again or Revoke token.