This repository provides a production-oriented, headless Docker Compose setup for OneDev: a self-hosted Git server with CI/CD, Kanban, package management, and build agents.
The setup is designed so you can run one command, get a working OneDev instance, and manage the full lifecycle safely without committing secrets.
make upAfter make up, OneDev is available from the browser and ready for the generated admin login:
- OneDev server pinned to
1dev/server:16.0.1 - PostgreSQL
17-alpineas the external production database - Dedicated OneDev runner using
1dev/agent:latest - Headless first-run provisioning
- Remote Docker executor auto-created in OneDev
- Local secrets generated automatically and never overwritten
- Makefile-based lifecycle commands
- Docker socket access isolated to the runner, not the main OneDev server
The production stack uses three services:
onedev: the web UI, Git server, API, build scheduler, and application logicpostgres: persistent external database for OneDev metadataonedev-agent: dedicated CI/CD runner that executes Docker-based jobs
The main OneDev server does not mount /var/run/docker.sock. Only the runner does. This keeps CI/CD Docker privileges away from the application server container and is safer than running builds directly on the server.
The provisioner configures OneDev with:
- system server URL
- SSH root URL
- agent token
- online runner
remote-dockerjob executor
docker-compose.production.yml: recommended production stackMakefile: operator interface.env.production.example: production environment templatescripts/provision-production.sh: headless provisioning script used bymake upscripts/create-agent-token.sh: creates an agent token if missingsecrets/.gitkeep: keeps the secrets directory in git
Generated local files:
.envsecrets/db_passwordsecrets/admin_passwordsecrets/onedev_agent_token
These generated files are ignored by git and must not be committed.
- Linux or macOS host
- Docker Engine
- Docker Compose v2
makebashcurlopensslpython3
For Linux production hosts, Docker should be installed as a system service. For rootless Docker, set:
DOCKER_SOCK=/path/to/rootless/docker.sockUsually this is:
DOCKER_SOCK=$XDG_RUNTIME_DIR/docker.sockFor local testing:
make upThis command is safe to run on a clean machine. It will:
- create
.envfrom.env.production.exampleif missing - create required secrets if missing
- keep existing secrets if they already exist
- start PostgreSQL and OneDev
- configure OneDev system URLs
- create an agent token if missing
- start the runner
- create/update the
remote-dockerexecutor - verify HTTP, PostgreSQL, executor, and runner status
After startup, open:
http://localhost:6610
The admin username comes from:
ONEDEV_ADMIN_USER=adminThe generated admin password is stored locally in:
secrets/admin_password
For a real production server, edit .env before running make up:
ONEDEV_SERVER_URL=https://git.example.com
ONEDEV_SSH_ROOT_URL=ssh://git.example.com:6611
ONEDEV_SYSTEM_SERVER_URL=https://git.example.com
ONEDEV_SYSTEM_SSH_ROOT_URL=ssh://git.example.com:6611
ONEDEV_AGENT_SERVER_URL=https://git.example.comUse real DNS, TLS, and firewall rules. Port 6611 is used for Git over SSH and must be reachable if SSH clone/push is required.
For local Docker-only testing, the default internal URLs are intentional:
ONEDEV_SYSTEM_SERVER_URL=http://onedev:6610
ONEDEV_SYSTEM_SSH_ROOT_URL=ssh://onedev:6611
ONEDEV_AGENT_SERVER_URL=http://onedev:6610These allow the runner container to clone repositories from the OneDev server over the Docker network.
Show commands:
make helpStart or provision:
make upStop containers while keeping data and secrets:
make downRestart and reprovision:
make restartShow container status:
make statusFollow logs:
make logs
make logs-server
make logs-runnerVerify runtime health:
make verifyOpen shells:
make shell-server
make shell-runner
make db-shellPull configured images:
make pullValidate rendered Compose config:
make configStop containers and remove Docker volumes, while keeping .env, database password, and admin password:
make destroymake destroy removes secrets/onedev_agent_token because agent tokens are stored server-side in the OneDev database. After the database volume is deleted, the old token is no longer valid and must be regenerated on the next make up.
Remove containers, volumes, .env, and local secret files:
make destroy-secretsFull local reset from scratch:
make resetUse destructive commands carefully. They remove application data stored in Docker volumes.
The runner is provisioned automatically as:
runner-01
The job executor is provisioned automatically as:
remote-docker
In .onedev-buildspec.yml, reference it like this:
version: 27
jobs:
- name: Hello World
jobExecutor: remote-docker
steps:
- !CheckoutStep
name: checkout
cloneCredential: !DefaultCredential {}
withLfs: false
withSubmodules: false
condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL
- !CommandStep
name: Print Environment
runInContainer: true
image: alpine:latest
interpreter: !DefaultInterpreter
commands:
- apk add --no-cache git
- echo "Hello from OneDev"
- pwd
- ls -la
- git rev-parse HEAD
useTTY: false
condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL
triggers:
- !BranchUpdateTrigger {}
retryCondition: never
maxRetries: 3
retryDelay: 30
timeout: 3600Secrets are local runtime files only.
Ignored by git:
.env
secrets/*
!secrets/.gitkeepDo not commit:
secrets/admin_passwordsecrets/db_passwordsecrets/onedev_agent_token.env
Secrets are generated only if missing. Existing secrets are preserved by default.
To rotate only the agent token:
make rotate-agent-tokenBack up both:
- PostgreSQL data volume:
onedev_postgres_data - OneDev data volume:
onedev_onedev_data
Recommended backup approach:
docker compose -f docker-compose.production.yml exec -T postgres \
pg_dump -U onedev -d onedev > backups/onedev-$(date +%F).sqlAlso back up OneDev data:
docker run --rm \
-v onedev_onedev_data:/data:ro \
-v "$PWD/backups:/backup" \
alpine tar czf /backup/onedev-data-$(date +%F).tar.gz -C /data .Create backups before upgrades, migrations, or destructive operations.
- Back up PostgreSQL and OneDev volumes.
- Update image versions in
.env:
ONEDEV_IMAGE=1dev/server:<version>
ONEDEV_AGENT_IMAGE=1dev/agent:latest- Pull and restart:
make pull
make up
make verifyOneDev agents are designed to be updated by the server as needed, but using the latest 1dev/agent image is recommended.
Official OneDev documentation says OneDev can run on a small 2 CPU / 2 GB RAM machine. That is suitable for evaluation, small teams, or light Git hosting.
This stack is designed for small to mid-size production use. A realistic starting point:
4 CPU / 8 GB RAM: small team, light CI/CD8 CPU / 16 GB RAM: moderate team, more repositories, regular CI/CD- separate runner hosts: recommended when CI jobs are heavy or untrusted
- fast SSD storage: strongly recommended for Git repositories and PostgreSQL
For approximately 500 registered users, this setup can be appropriate if usage is moderate:
- not all users are active concurrently
- CI/CD concurrency is limited
- repositories are not extremely large
- PostgreSQL and Docker volumes are on SSD storage
- runners are scaled separately for build load
Do not treat 500 users as a hard guarantee. Capacity depends more on concurrent activity, repository size, build frequency, artifact volume, and CI job weight than on registered user count.
For heavier production usage, scale by:
- adding more OneDev agents on separate machines
- increasing CPU/RAM for PostgreSQL and OneDev
- using external managed PostgreSQL
- placing Git/data volumes on high-performance SSD storage
- limiting concurrent builds per runner
- monitoring CPU, RAM, disk I/O, PostgreSQL latency, and Docker job queue time
The Docker socket is powerful. Any container with /var/run/docker.sock can effectively control the Docker host.
This stack reduces risk by:
- not mounting Docker socket into the main OneDev server
- mounting Docker socket only into the runner container
- using a remote Docker executor
- separating CI execution from the application server container
For untrusted users or public registration:
- disable open signup unless required
- restrict who can create projects and edit build specs
- run runners on dedicated worker machines
- do not run untrusted builds on the same host as production data
- consider Kubernetes executors or isolated runner hosts for stronger boundaries
Check stack health:
make status
make verifyView server logs:
make logs-serverView runner logs:
make logs-runnerIf checkout fails with a domain error such as:
Could not resolve host: git.example.com
then OneDev system URLs are wrong for the runner. For local testing, use:
ONEDEV_SYSTEM_SERVER_URL=http://onedev:6610
ONEDEV_SYSTEM_SSH_ROOT_URL=ssh://onedev:6611
ONEDEV_AGENT_SERVER_URL=http://onedev:6610Then rerun:
make upIf jobs say:
No applicable executor discovered for current job
ensure your buildspec uses:
jobExecutor: remote-dockerand run:
make verifyIf the runner is not online:
make logs-runner
make rotate-agent-token
