Skip to content

Instantly share code, notes, and snippets.

@sax
Last active January 14, 2026 22:46
Show Gist options
  • Select an option

  • Save sax/c790482d51b5a6a1b240c87b87517f08 to your computer and use it in GitHub Desktop.

Select an option

Save sax/c790482d51b5a6a1b240c87b87517f08 to your computer and use it in GitHub Desktop.
GitHub Actions for building a Phoenix release as a FreeBSD pkg

GitHub Actions for building a Phoenix release as a FreeBSD pkg

This example builds a release in FreeBSD 14.3, packages it into a .pkg file, and updates a pkg repo stored in S3. It depends upon the freebsd hex package.

Deployment uses Tailscale to ssh into the application jail(s), sync changes via the AWS cli, then runs pkg update -y <name> and service <name> restart.

Required secrets

Packaging:

  • AWS_KEY_ID
  • AWS_SECRET_ACCESS_KEY
  • AWS_BUCKET
# /usr/local/etc/pkg/repos/<app_name>.conf
<app name>: {
enabled: yes,
url: file:///var/app/<...pkg_repo_name>
}
ABI="$(/usr/sbin/pkg config ABI)"
REPO="s3://${AWS_BUCKET}/${APP_NAME}/${PKG_REPO_NAME}/${ABI}/latest"
echo "==== Syncing repo; ${REPO}"
aws s3 sync "${REPO}" /var/app/${PKG_REPO_NAME} --delete
echo "==== Upgrading ${APP_NAME}"
/usr/sbin/pkg upgrade -y ${APP_NAME}
echo "==== Restarting service"
if /usr/sbin/service ${APP_NAME} status; then
/usr/sbin/service ${APP_NAME} restart
else
/usr/sbin/service ${APP_NAME} start
fi
defmodule My.MixProject do
use Mix.Project
def application,
do: [
extra_applications: [:tls_certificate_check, :asn1, :logger, :os_mon, :plug, :runtime_tools],
mod: {My.Application, []}
]
def project,
do: [
# ...
deps: deps(),
freebsd: freebsd(),
version: version()
]
## ## ##
defp deps,
do: [
# ...
{:freebsd, "~> 0.6", github: "sax/ex_freebsd", branch: "status", runtime: false}
]
defp description,
do: "Server for <domain>; sha: #{System.get_env("GITHUB_SHA")}"
defp freebsd,
do: %{
maintainer: "support@<domain>",
user: "<user to run service as>",
description: description()
}
defp releases do
[
app: [
applications: [os_mon: :permanent, runtime_tools: :permanent],
include_executables_for: [:unix],
strip_beams: false
]
]
end
def version do
minor = System.get_env("GITHUB_RUN_NUMBER") || "0"
"1.#{minor}.0"
end
end
name: Build FreeBSD Pkg
run-name: "${{ github.workflow }}: [#${{ github.event.workflow_run.run_number }}] ${{ github.event.workflow_run.display_title }}"
on:
workflow_run:
workflows: [Test & Audit]
types: [completed]
branches: [main]
concurrency: pkg
env:
APP_NAME: "my_app"
PKG_REPO_NAME: "my_repo"
jobs:
build_package_14:
name: "Build FreeBSD 14 package"
runs-on: ubuntu-latest
timeout-minutes: 20
if: ${{ github.event.workflow_run.conclusion == 'success' }}
env:
MIX_ENV: prod
FREEBSD_ABI: "FreeBSD:14:amd64"
FREEBSD_VERSION: "14.3"
steps:
- name: Setup Host for VM
run: |
echo "Starting storage:"
sudo df -h
echo "Removing Android SDKs and Tools"
sudo rm -rf /usr/local/lib/android
echo "Reduce swappiness"
sudo sysctl vm.swappiness=10
echo "Available storage:"
sudo df -h
- uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Build in FreeBSD
id: build
uses: vmactions/freebsd-vm@v1.3.7
with:
copyback: false
release: ${{ env.FREEBSD_VERSION }}
envs: 'GITHUB_RUN_NUMBER GITHUB_SHA MIX_ENV APP_NAME PKG_REPO_NAME'
usesh: true
prepare: |
mkdir -p /usr/local/etc/pkg/repos
echo 'FreeBSD: { url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest" }' > /usr/local/etc/pkg/repos/FreeBSD.conf
pkg update -f -q
pkg upgrade -y
pkg install -y \
cmake \
gcc \
git \
gmake \
node25 \
npm-node25
mkdir -p /usr/local/etc/ssl
cat /etc/ssl/certs/*.0 > /etc/ssl/cert.pem
## Install specific versions of Elixir/OTP from: https://github.com/goetic/beam-ports
cat <<'EOF' > /usr/local/etc/pkg/repos/beam.conf
beam: {
url: "https://goetic-pkg.s3.us-east-2.amazonaws.com/beam/freebsd/${ABI}/latest",
signature_type: "pubkey",
pubkey: "/usr/local/etc/ssl/beam-ports.pub",
enabled: yes
}
EOF
cat <<EOF > /usr/local/etc/ssl/beam-ports.pub
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzRpocI3URq965dK7l3p2
sEOKWwA+J1HTrbdG+K4VCNfB2b8tui37sp8Q24OMknfJixvey/kVEhWemN6cLHrV
Uj5qHOfTKm8kdAaZKaMDQxnVi2fqP36Sbrfm5Ln2uU6Nd3168sOt6STCKjfv3OKU
Evv+ltQ5q06G5sbTIU9Sl+B69/rnIjjELzyqmmb7/u93Ispgf+MlEUTxJi2Gl1ki
2dMuuGJopo1zGOD+j0CIeSIUVYuhl6uN4QKUz30FZjMpFOGd4gvR9K5VC9FghHcp
YFKefsWUaGlfisfwM4ZSa9VKymwVtREvdUbe/7J9az7c7BEgdTQMDlMyeOUEe9mt
CtztuxAB/WzwnCvnNmepda/7372U6ijlCLO6/9+kwX/yX72G6IioBZ1DZh4NS5eM
UV1Yd65ce0JrCtASYgoGrsmo1ch6NMOWbRguLG60H32vGgpbIAN6oLmLpFdbmlvq
LurZVf+2qvusX7vZN1sA77qePWWtcKgslrRvN3IN9eMGnd42rs0cNjsB8J8VqLPL
DGOapzEq8d01hlDWSSpBBW1xoi90aql0vXhjN2kqxtK7ZuNsY1514TTomv2hubYl
l5tyuSbQ1rG/6SPZblXI6u/roZoZIZ6zQ3H6x4pnaq/2Y56kpG0bR2eGs6YX0uwM
qLtzysaWPOVJ/Qp4daWoUnECAwEAAQ==
-----END PUBLIC KEY-----
EOF
pkg update
pkg install -y \
elixir-1.19.5-otp-28 \
erlang-28.3
run: |
export PATH=/usr/local/lib/erlang-28.3/bin:$PATH
pwd
mix local.hex --force \
&& mix local.rebar --force
mix deps.get --only $MIX_ENV
npm install -g pnpm
npm ci --prefix assets
mix deps.compile --force freebsd
mix compile --force
cd ../..
- name: Create release
run: |
ssh freebsd << EOF
export PATH=/usr/local/lib/erlang-28.3/bin:$PATH
cd $GITHUB_WORKSPACE
mix assets.deploy
mix release
EOF
- name: Sync from S3
run: aws s3 sync s3://${{ secrets.AWS_BUCKET}}/${{ env.APP_NAME }}/latest/ ./freebsd_pkg
- name: Create package
run: |
scp -r ./freebsd_pkg freebsd:$GITHUB_WORKSPACE/freebsd_pkg
ssh freebsd << EOF
export PATH=/usr/local/lib/erlang-28.3/bin:$PATH
echo "== Working dir: $(pwd)"
echo "== Creating package"
mix freebsd.pkg
echo "== Creating $GITHUB_WORKSPACE/$PKG_REPO_NAME/All"
mkdir -p $GITHUB_WORKSPACE/$PKG_REPO_NAME/All
## Retain the previous 9 releases
find $GITHUB_WORKSPACE/freebsd_pkg -type f -name "$APP_NAME-*.pkg" \
| sort -r \
| head -9 \
| xargs -J% -n1 cp % $GITHUB_WORKSPACE/$PKG_REPO_NAME/All
cp $APP_NAME-*.pkg $GITHUB_WORKSPACE/$PKG_REPO_NAME/All/
echo "== Packinging repo"
pkg repo $GITHUB_WORKSPACE/$PKG_REPO_NAME
EOF
scp -r freebsd:$GITHUB_WORKSPACE/${{ env.APP_NAME }}-*.pkg .
scp -r freebsd:$GITHUB_WORKSPACE/${{ env.PKG_REPO_NAME }} ./
- name: Sync latest pkg to S3
run: aws s3 cp ${{ env.APP_NAME }}-*.pkg s3://${{ secrets.AWS_BUCKET}}/${{ env.APP_NAME }}/latest/${{ env.FREEBSD_ABI }}/
- name: Sync pkg repo to S3
run: aws s3 sync ${{ env.PKG_REPO_NAME }} s3://${{ secrets.AWS_BUCKET}}/${{ env.APP_NAME }}/${{ env.PKG_REPO_NAME }}/${{ env.FREEBSD_ABI }}/latest --delete
build_package_15:
name: "Build FreeBSD 15 package"
runs-on: ubuntu-latest
timeout-minutes: 20
if: ${{ github.event.workflow_run.conclusion == 'success' }}
env:
MIX_ENV: prod
FREEBSD_ABI: "FreeBSD:15:amd64"
FREEBSD_VERSION: "15.0"
steps:
- name: Setup Host for VM
run: |
echo "Starting storage:"
sudo df -h
echo "Removing Android SDKs and Tools"
sudo rm -rf /usr/local/lib/android
echo "Reduce swappiness"
sudo sysctl vm.swappiness=10
echo "Available storage:"
sudo df -h
- uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Build in FreeBSD
id: build
uses: vmactions/freebsd-vm@v1.3.7
with:
copyback: false
release: ${{ env.FREEBSD_VERSION }}
envs: 'GITHUB_RUN_NUMBER GITHUB_SHA MIX_ENV APP_NAME PKG_REPO_NAME'
usesh: true
prepare: |
mkdir -p /usr/local/etc/pkg/repos
echo 'FreeBSD: { url: "http://pkg.FreeBSD.org/${ABI}/latest" }' > /usr/local/etc/pkg/repos/FreeBSD.conf
pkg update -f -q
pkg upgrade -y
pkg install -y \
cmake \
gcc \
git \
gmake \
node25 \
npm-node25
mkdir -p /usr/local/etc/ssl
cat /etc/ssl/certs/*.0 > /etc/ssl/cert.pem
## Install specific versions of Elixir/OTP from: https://github.com/goetic/beam-ports
cat <<'EOF' > /usr/local/etc/pkg/repos/beam.conf
beam: {
url: "https://goetic-pkg.s3.us-east-2.amazonaws.com/beam/freebsd/${ABI}/latest",
signature_type: "pubkey",
pubkey: "/usr/local/etc/ssl/beam-ports.pub",
enabled: yes
}
EOF
cat <<EOF > /usr/local/etc/ssl/beam-ports.pub
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzRpocI3URq965dK7l3p2
sEOKWwA+J1HTrbdG+K4VCNfB2b8tui37sp8Q24OMknfJixvey/kVEhWemN6cLHrV
Uj5qHOfTKm8kdAaZKaMDQxnVi2fqP36Sbrfm5Ln2uU6Nd3168sOt6STCKjfv3OKU
Evv+ltQ5q06G5sbTIU9Sl+B69/rnIjjELzyqmmb7/u93Ispgf+MlEUTxJi2Gl1ki
2dMuuGJopo1zGOD+j0CIeSIUVYuhl6uN4QKUz30FZjMpFOGd4gvR9K5VC9FghHcp
YFKefsWUaGlfisfwM4ZSa9VKymwVtREvdUbe/7J9az7c7BEgdTQMDlMyeOUEe9mt
CtztuxAB/WzwnCvnNmepda/7372U6ijlCLO6/9+kwX/yX72G6IioBZ1DZh4NS5eM
UV1Yd65ce0JrCtASYgoGrsmo1ch6NMOWbRguLG60H32vGgpbIAN6oLmLpFdbmlvq
LurZVf+2qvusX7vZN1sA77qePWWtcKgslrRvN3IN9eMGnd42rs0cNjsB8J8VqLPL
DGOapzEq8d01hlDWSSpBBW1xoi90aql0vXhjN2kqxtK7ZuNsY1514TTomv2hubYl
l5tyuSbQ1rG/6SPZblXI6u/roZoZIZ6zQ3H6x4pnaq/2Y56kpG0bR2eGs6YX0uwM
qLtzysaWPOVJ/Qp4daWoUnECAwEAAQ==
-----END PUBLIC KEY-----
EOF
pkg update
pkg install -y \
elixir-1.19.5-otp-28 \
erlang-28.3
run: |
export PATH=/usr/local/lib/erlang-28.3/bin:$PATH
pwd
mix local.hex --force \
&& mix local.rebar --force
mix deps.get --only $MIX_ENV
npm install -g pnpm
npm ci --prefix assets
mix deps.compile --force freebsd
mix compile --force
cd ../..
- name: Create release
run: |
ssh freebsd << EOF
export PATH=/usr/local/lib/erlang-28.3/bin:$PATH
cd $GITHUB_WORKSPACE
mix assets.deploy
mix release
EOF
- name: Sync from S3
run: aws s3 sync s3://${{ secrets.AWS_BUCKET}}/${{ env.APP_NAME }}/latest/ ./freebsd_pkg
- name: Create package
run: |
scp -r ./freebsd_pkg freebsd:$GITHUB_WORKSPACE/freebsd_pkg
ssh freebsd << EOF
export PATH=/usr/local/lib/erlang-28.3/bin:$PATH
echo "== Working dir: $(pwd)"
echo "== Creating package"
mix freebsd.pkg
echo "== Creating $GITHUB_WORKSPACE/$PKG_REPO_NAME/All"
mkdir -p $GITHUB_WORKSPACE/$PKG_REPO_NAME/All
## Retain the previous 9 releases
find $GITHUB_WORKSPACE/freebsd_pkg -type f -name "$APP_NAME-*.pkg" \
| sort -r \
| head -9 \
| xargs -J% -n1 cp % $GITHUB_WORKSPACE/$PKG_REPO_NAME/All
cp $APP_NAME-*.pkg $GITHUB_WORKSPACE/$PKG_REPO_NAME/All/
echo "== Packinging repo"
pkg repo $GITHUB_WORKSPACE/$PKG_REPO_NAME
EOF
scp -r freebsd:$GITHUB_WORKSPACE/${{ env.APP_NAME }}-*.pkg .
scp -r freebsd:$GITHUB_WORKSPACE/${{ env.PKG_REPO_NAME }} ./
- name: Sync latest pkg to S3
run: aws s3 cp ${{ env.APP_NAME }}-*.pkg s3://${{ secrets.AWS_BUCKET}}/${{ env.APP_NAME }}/latest/${{ env.FREEBSD_ABI }}/
- name: Sync pkg repo to S3
run: aws s3 sync ${{ env.PKG_REPO_NAME }} s3://${{ secrets.AWS_BUCKET}}/${{ env.APP_NAME }}/${{ env.PKG_REPO_NAME }}/${{ env.FREEBSD_ABI }}/latest --delete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment