Contributing Quickstart
A 10-minute guide to building and testing your first FreeBSD container image.
Prerequisites
- FreeBSD (physical host or VM with Podman access)
- Python 3.11+ and git
- Podman with ocijail runtime
- A GitHub account (for forking and PRs)
Check your environment
After installing dbuild, run dbuild ci-test-env to verify all required tools, networking, and jail support are in place.
Setup (~5 minutes)
1. Clone the repos
# The main project (scripts, docs, version tracking)
git clone https://github.com/daemonless/daemonless
# The build tool
git clone https://github.com/daemonless/dbuild
# An image repo to use as a reference
git clone https://github.com/daemonless/tautulli
2. Install dependencies
# Core build tools
pkg install podman buildah skopeo jq trivy py311-pyyaml
# Optional: for screenshot-based visual regression testing
pkg install chromium py311-selenium py311-scikit-image
3. Install dbuild
Or manually:
3. Verify your environment
All required checks should pass. Optional tools (like podman-compose) are only needed for multi-service stacks.
Your First Image (~5 minutes)
1. Scaffold a new image
This creates:
myapp/
├── Containerfile # Build from upstream binaries (:latest tag)
├── Containerfile.pkg # Build from FreeBSD packages (:pkg tag)
├── .daemonless/
│ └── config.yaml # Build + test configuration
├── .woodpecker.yaml # CI/CD pipeline (or .github/workflows/)
└── root/ # Files copied into container
└── etc/
├── cont-init.d/ # Initialization scripts
└── services.d/ # s6 service definitions
└── myapp/
└── run # Service start script
Repository structure
Each image is a standalone git repo with this layout. See the Development Guide for a detailed walkthrough.
2. Build it
3. Test it
4. Run it manually
podman run -d --name myapp \
-p 8080:8080 \
-e PUID=1000 -e PGID=1000 \
-v /tmp/myapp-config:/config \
localhost/myapp:build-latest
Contribution Types
| Type | Description | Where |
|---|---|---|
| New images | Package a new application as a FreeBSD container | New repo under daemonless/ |
| Image fixes | Bug fixes or improvements to existing images | The image's repo |
| Tooling | Improvements to dbuild or CIT | daemonless/dbuild |
| Documentation | Guides, corrections, image docs | daemonless/daemonless-io |
Image Checklist
Before submitting a new image, verify:
- [x]
Containerfilewith required labels (io.daemonless.port,io.daemonless.category,io.daemonless.packages) - [x]
Containerfile.pkgwith matching labels (keep both in sync) - [x]
root/etc/services.d/<app>/run— s6 service script usings6-setuidgid bsd - [x]
.daemonless/config.yamlwith CIT test configuration - [x] CI pipeline configured (
.woodpecker.yamlor.github/workflows/) - [x] Upstream license verified — check the SPDX identifier
- [x]
dbuild build && dbuild testpasses locally
Never guess a license
Always check the upstream repository's LICENSE file and verify the SPDX identifier. Common licenses: MIT, Apache-2.0, GPL-3.0, AGPL-3.0, BSD-3-Clause.
Conventions
Containerfile rules
- Use
fetch, notcurl— FreeBSD base includesfetch - Clean the pkg cache:
pkg clean -ay && rm -rf /var/cache/pkg/* - Set ownership:
chown -R bsd:bsd /config /app - Use
ARGforBASE_VERSION,PACKAGES, andVERSION - Use
.j2templates — rundbuild generateafter changes
Containerfile patterns
Daemonless uses three Containerfile patterns: standard (upstream binaries), package (FreeBSD packages), and multi-stage (compiled apps). See the Development Guide for templates of each.
Runtime conventions
- Run services as
bsduser vias6-setuidgid bsd - Support
PUID,PGID, andTZenvironment variables - Use
/configas the configuration volume - Use
execin run scripts for proper signal handling
Labels
Every image needs at minimum:
LABEL io.daemonless.port="8080"
LABEL io.daemonless.category="Utilities"
LABEL io.daemonless.packages="${PACKAGES}"
See the Labels Reference for the full list of io.daemonless.* and OCI labels.
Testing
All images must pass CIT (Container Integration Testing) before push. CIT has four cumulative modes:
| Mode | Checks | Use case |
|---|---|---|
shell |
Container starts, exec works | Base images, CLI tools |
port |
Shell + TCP port listening | Network services |
health |
Port + HTTP endpoint responds | Web apps |
screenshot |
Health + visual regression | Web UIs |
Configure your test mode in .daemonless/config.yaml:
Run tests locally:
Choosing a test mode
Most web applications should use health mode. Use screenshot mode for apps with a web UI where visual regressions matter. See the Quality Gates (CIT) guide for the full configuration reference.
Submitting Changes
- Fork the image repo on GitHub
- Branch from
main - Build and test locally with
dbuild build && dbuild test - Open a PR — CI runs CIT automatically
- All CIT gates must pass before merge
Get Help
Stuck on something? Join us on Discord — it's the fastest way to get help from the community.
Further Reading
- Development Guide — Full architecture, labels reference, Containerfile patterns
- dbuild — Complete build engine documentation
- Quality Gates (CIT) — Test modes, configuration, visual regression
- CI/CD Pipeline — CI integration and pipeline details
- Image Tagging — Tag conventions and version strategy
- OCI Compliance — Label standards and SBOM