Multi-tenant goat-farm management — herd, health, breeding, milk, sales, pedigree. Self-host.
2.0K
Self-hostable goat-farm management software — herd records, health, breeding, milk, sales, pedigree, calendar, and alerts. Built for a single farm or a small group of homesteads sharing an instance.
If you're looking for the hosted version, that's at https://goatlab.app. The source code for this image lives at https://github.com/chrisdfennell/GoatLabSaaS and is MIT-licensed.
You need Docker and about five minutes. GoatLab uses SQL Server 2022 as its database, but you don't have to install anything separately — the included compose file brings up both the app and the database together.
Grab the two files you need (compose definition + env template) into an empty directory:
mkdir goatlab && cd goatlab
curl -O https://raw.githubusercontent.com/chrisdfennell/GoatLabSaaS/main/docker-compose.oss.yml
curl -o .env https://raw.githubusercontent.com/chrisdfennell/GoatLabSaaS/main/.env.example
Open .env and pick a strong SA_PASSWORD — at least 8 characters with
upper-case, lower-case, a digit, and a symbol. SQL Server is picky about
this and refuses to start without it.
Then:
docker compose -f docker-compose.oss.yml up -d
Open http://localhost:8090. The first email you register with becomes the super-admin for the whole instance, so make sure it's yours.
That's it — you're running. No need to clone the source repository unless you want to read or modify the code (see https://github.com/chrisdfennell/GoatLabSaaS for that).
Two kinds of tags get published:
latest — moves with every release. Convenient for getting started;
not recommended for production because it shifts under you.1.0.0, 1.0.1, … — exact-version tags. Pin one in production so
upgrades only happen when you change the tag.By default the compose file pulls fennch/goatlab:latest. To pin a version,
set GOATLAB_IMAGE_TAG=1.0.0 in your .env.
Every tag is multi-arch (linux/amd64 + linux/arm64), so the same tag
works on a regular x86 server, a Raspberry Pi, or an arm64 NAS without you
having to think about it.
If you only ever use GoatLab over http://localhost, skip this section.
Once you put a real domain in front of it — Caddy, Nginx, Traefik, Cloudflare Tunnel, whatever — you need to tell GoatLab what domain to expect. Without this, passkeys and a few other browser-side features will refuse to work, because the browser sees one domain and the app expects another.
Add this to your .env:
WEBAUTHN_RP_ID=goats.example.com
WEBAUTHN_ORIGIN=https://goats.example.com
Restart the container and you're set.
Out of the box, GoatLab works without email. You can ignore this section until you want password resets, daily alert digests, or invitations to actually arrive in someone's inbox.
When you're ready, fill in the SMTP_* variables in .env. Most providers
— Brevo, SendGrid, AWS SES, or your own mail server — will give you the
host, port, username, and password to drop in.
One trap to avoid: don't set IDENTITY_REQUIRE_CONFIRMED_EMAIL=true before
SMTP actually works. If you do, the first user gets locked out because the
confirmation email goes nowhere and login refuses unconfirmed accounts.
Wire SMTP first, register a real account, then turn confirmation on.
The compose file creates four named Docker volumes:
goatlab-mssql — the database itself. The big one.goatlab-media — uploaded photos and documents.goatlab-backups — nightly database backups (.bak files).goatlab-dpkeys — ASP.NET keys for session cookies and passkeys.Back up the first three. The fourth regenerates if it disappears, but losing it logs everyone out and invalidates registered passkeys.
GoatLab runs a SQL backup every night at 04:00 UTC and writes it to
/app/backups inside the container (the goatlab-backups volume). You can
also kick one off manually from the /admin/health page.
For offsite copies, set the BACKUP_OFFSITE_* variables in .env. Anything
S3-compatible works — Backblaze B2, Wasabi, DigitalOcean Spaces, MinIO, or
plain AWS S3. The last-success timestamp shows on /admin/health so you
can confirm it's actually running.
docker compose -f docker-compose.oss.yml pull goatlab
docker compose -f docker-compose.oss.yml up -d goatlab
Database migrations run automatically the next time the container starts.
If one fails, the container refuses to come up rather than corrupting
anything — docker compose logs goatlab will tell you what went wrong.
This image runs in self-host mode by default, which is almost certainly what you want. Compared to goatlab.app, that means:
If you ever decide to run a paid service for multiple farms, you can flip
Saas__Enabled=true and the SaaS surface comes back. You'll just need to
wire up Stripe and SMTP at that point.
docker compose logs goatlab. Nine
times out of ten it's the SA password not meeting complexity, or the app
can't reach the database container.IDENTITY_REQUIRE_CONFIRMED_EMAIL=true before SMTP was working. Set it
back to false, log in, then turn it on once email is wired.WEBAUTHN_RP_ID doesn't match the
hostname in the browser's URL bar. They have to match exactly.For anything else, open an issue at https://github.com/chrisdfennell/GoatLabSaaS/issues. PRs welcome.
Content type
Image
Digest
sha256:7b8d753f5…
Size
175.9 MB
Last updated
4 months ago
docker pull fennch/goatlab