Documentation / onedev
OneDev
Installs OneDev: Git repositories with code review and pull requests, issues, and container, npm and Maven registries, in one server.
The chart provides:
- OneDev 17.1, for amd64 and arm64, with its database in a PostgreSQL of your
own, such as a release of the
postgresqlchart, reached over TLS with the server’s certificate checked; - repositories, packages and build artifacts on a volume that is kept when the release is uninstalled;
- the first administrator’s password and the database credentials taken from Secrets you create, which the chart never creates, changes or prints;
- Git over HTTPS through an Ingress, and Git over SSH on a Service of its own;
- no permission in the cluster, unless you enable the Kubernetes executor;
- a NetworkPolicy;
- OneDev’s image from the Mamluk registry, pinned by digest, and a Pod meeting
the
restrictedPod Security Standard with a read-only root filesystem.
OneDev’s own build pipelines are not the subject of this chart. For
continuous integration, use the concourse chart. See “Builds in the
cluster” below for what the Kubernetes executor would require.
Prerequisites
- Kubernetes 1.33 or later.
- A pull secret for
1x.axnamed1x-axin the release’s namespace, created from an image token as described in the access documentation. -
A PostgreSQL database owned by the user OneDev connects as. A release of the
postgresqlchart in the same namespace is the simplest:helm install pg tersane/postgresql --namespace git --create-namespace \ --set bootstrap.database=onedev --set bootstrap.owner=onedevIts
pg-postgresql-appSecret holds the user and password, and itspg-postgresql-caSecret the certificate authority OneDev checks the server against. -
A Secret holding the first administrator’s password:
kubectl --namespace git create secret generic onedev-admin \ --from-file=password=./password
Installation
helm install onedev tersane/onedev --namespace git \
--set externalURL=https://git.example.com \
--set admin.email=<administrator's email address> \
--set admin.existingSecret=onedev-admin \
--set database.host=pg-postgresql-rw \
--set database.existingSecret=pg-postgresql-app \
--set database.caSecret=pg-postgresql-ca \
--set ingress.className=<class> --set ingress.tlsSecret=<certificate Secret> \
--version <chart version>
The release’s resources are named <release>-onedev (here onedev-onedev),
or after the release alone if its name already contains onedev. OneDev
creates its tables when it first starts. Check that the Pod is ready:
kubectl --namespace git get pods -l app.kubernetes.io/instance=onedev
Then sign in at the external URL as root, or the name in admin.username.
The administrator, externalURL and ssh.url are read once, when OneDev
first creates its database. Changing these values afterwards changes nothing:
change them in OneDev itself, under Administration.
OneDev runs as one server. Running several servers as a cluster is a feature of OneDev’s enterprise edition, which this chart does not configure.
Database
OneDev connects with the JDBC driver it includes. database.sslmode is
verify-full by default: the connection is encrypted and the server’s
certificate is checked against the authority in database.caSecret, and must
name database.host. require encrypts without checking the certificate,
and disable does not encrypt.
Exposing OneDev
The chart’s Ingress serves the host in externalURL and sends everything to
OneDev. Whatever proxy is in front of OneDev must do three things, each
learnt on a OneDev in production:
- Not limit the request body. Pushes and package uploads can be any size.
- Buffer responses, with no small cap on the buffer. OneDev’s web
server closes a connection that has been idle for 30 seconds. A client
that pauses while downloading a large layer, unbuffered, has its download
cut. A proxy that buffers reads from OneDev at its own speed. nginx’s
default buffer cap of 1 GiB cuts every larger download: raise
proxy_max_temp_file_sizeabove your largest layer. - Answer range requests itself. OneDev ignores
Rangeand returns the whole file, so an interrupted pull starts again from the beginning. nginx does this withproxy_force_ranges on.
Do not lengthen idle timeouts on the Git and SSH paths to hide slow clients: CI systems poll repositories every few seconds, and long-lived idle connections then accumulate.
With service.type: NodePort and the Ingress disabled, OneDev is reached on
the nodes over plain HTTP, which only suits a test.
Git over SSH
ssh:
enabled: true
url: ssh://git.example.com:22
service:
type: LoadBalancer
SSH has a Service of its own, <release>-onedev-ssh, so it can be exposed
apart from the web interface: as a LoadBalancer, a NodePort, or through an
Ingress controller that carries TCP. ssh.url is the address OneDev shows in
clone commands. Limit who reaches it with networkPolicy.sshFrom.
Trusted authorities
OneDev trusts the system’s authorities and those in trustedCA: an OIDC
provider’s, a mail server’s, or a remote repository’s that OneDev mirrors.
Each entry names a Secret in the release’s namespace and the key holding the
certificate. The database’s authority is added with them.
Builds in the cluster
This section matters only if you set kubernetesExecutor.enabled. It is
off by default, and Concourse, through the concourse chart, is the CI we
recommend.
OneDev’s Kubernetes executor runs each build as Pods in the cluster. To do
so, OneDev 17.1 creates a new namespace for every build, labelled with the
Pod Security level privileged, and deletes it afterwards. There is no
setting to make it use a namespace of your choosing.
Enabling it gives OneDev a ClusterRole with exactly what the executor does:
| Resource | Verbs |
|---|---|
| namespaces | create, delete, list |
| pods | create, delete, get, watch |
| pods/log | get |
| pods/exec | create |
| services, secrets, configmaps, persistentvolumeclaims | create, delete |
| clusterrolebindings | create, delete |
and, by a Role in kube-system only, list on Services there, which the
check OneDev runs before every build (kubectl cluster-info) needs.
The chart grants no read access to Secrets, nodes or deployments, which OneDev’s own chart grants and its executor does not use.
Every build Pod also runs OneDev’s helper image, 1dev/k8s-helper, at the
version the installed OneDev names in its code, from Docker Hub. OneDev
offers no setting to change it, so a cluster without access to Docker Hub
needs a containerd mirror for it, as for the images the build steps name.
This is cluster-admin in effect. Creating Pods in any namespace means running as any ServiceAccount in that namespace and mounting any of its Secrets, including in system namespaces. Anyone who can change a build specification or the executor’s settings in OneDev holds that power. Enable it only on a cluster dedicated to builds, if at all.
The Compliance and Assurance Convoy refuses these builds. With its policies at Deny, every build namespace is privileged by OneDev’s own design and is created afresh, so it can be neither brought within the policies nor exempted by name. Builds through the executor fail on such a cluster.
Upgrades
Each chart version pins OneDev’s image by digest. When the image changes, an init container upgrades the installation on the volume before OneDev starts, and OneDev migrates its database when it starts. Where the new version changes the database’s structure, the upgrade also exports the database to the volume and imports it again with the new program, so it takes longer the larger the database. Neither is reversed by rolling back the chart, so back up the database and the volume first, and read OneDev’s release notes before moving to a new major version. The server is stopped before the new one starts: there is one server, and an upgrade has a short outage.
Backups and restore
A complete backup is two things, taken together:
- The database: the
postgresqlchart’s backups to S3-compatible storage, with continuous WAL archiving. - The volume
<release>-onedev-data, which holds the repositories, the packages and the build artifacts: a snapshot through your storage’s CSI driver, or a copy of the volume. A volume copy older than the database backup lacks content the database refers to, so take it after the database backup.
To restore, recover the database into a new postgresql release, make the
volume copy available as a claim, and install OneDev with
persistence.existingClaim naming it.
Network policy
The NetworkPolicy admits HTTP to OneDev from networkPolicy.ingressFrom, set
it to your Ingress controller, and SSH from networkPolicy.sshFrom. Either
empty admits any source, as a NodePort or LoadBalancer requires.
Pod Security
The Pod meets the restricted Pod Security Standard: OneDev runs as user
10000 with a read-only root filesystem, no privilege escalation, every
capability dropped and the runtime’s default seccomp profile. It holds no
service account token unless the Kubernetes executor is enabled.
OneDev’s image starts as root from a script in /root, and copies its
installation onto the volume with a program that writes beside itself. The
chart instead starts OneDev directly: an init container copies the
installation to scratch space and runs OneDev’s upgrade from there, which
fills the volume on the first start and upgrades it when the image changes.
The Pod shares one process namespace, so the Pod’s pause process is its first process and would reap any process left without a parent. This is a precaution: our tests have not produced such processes, with or without it, so they do not show that it is needed.
Uninstalling
helm uninstall onedev --namespace git
The volume is kept. The database is yours, and is untouched.
Parameters
OneDev
| Parameter | Description | Default |
|---|---|---|
externalURL |
The address users reach OneDev at, with its scheme; required; read once | "" |
image.repository / image.tag |
The image | 1x.ax/tersane/core/onedev/server / the release’s |
imagePullPolicy |
Pull policy | IfNotPresent |
imagePullSecrets |
Pull secrets | [{name: 1x-ax}] |
admin.username / admin.email |
The first administrator; the email is required; read once | root / "" |
admin.existingSecret / admin.passwordKey |
The Secret with the first administrator’s password; required | "" / password |
server.maxMemoryPercent |
Percentage of the memory limit for the Java heap | 50 |
server.resources |
Resources of the OneDev container | 500m and 2Gi requested, 4 CPUs and 4Gi limits |
Database and storage
| Parameter | Description | Default |
|---|---|---|
database.host / database.port / database.name |
The PostgreSQL server and database; host required | "" / 5432 / onedev |
database.existingSecret / database.usernameKey / database.passwordKey |
The Secret with the user and password; required | "" / username / password |
database.sslmode |
disable, require, verify-ca or verify-full |
verify-full |
database.caSecret / database.caKey |
The server’s certificate authority | "" / ca.crt |
database.maxPoolSize |
Connections OneDev holds | 25 |
persistence.size / persistence.storageClass |
The volume | 100Gi / the default class |
persistence.existingClaim |
A claim of your own instead | "" |
trustedCA |
Further authorities, as {secret, key} entries |
[] |
Access
| Parameter | Description | Default |
|---|---|---|
service.type / service.port / service.nodePort |
The web Service: ClusterIP or NodePort |
ClusterIP / 80 / "" |
ssh.enabled |
Git over SSH on a Service of its own | false |
ssh.url |
The SSH address shown in clone commands; required with SSH; read once | "" |
ssh.service.type / .port / .nodePort |
The SSH Service | ClusterIP / 22 / "" |
ingress.enabled / ingress.className |
The Ingress | true / "" |
ingress.annotations / ingress.tlsSecret |
Its annotations and certificate | {} / "" |
ingress.serviceAnnotations |
Annotations on the web Service | {} |
networkPolicy.enabled |
The NetworkPolicy | true |
networkPolicy.ingressFrom / networkPolicy.sshFrom |
Sources admitted to HTTP and SSH; empty admits any | [] / [] |
kubernetesExecutor.enabled |
Builds as Pods in the cluster; cluster-admin in effect; see above | false |
Pod
| Parameter | Description | Default |
|---|---|---|
podSecurityContext |
The Pod’s security context | user 10000, restricted |
securityContext |
Each container’s security context | read-only root, nothing added |
nodeSelector / tolerations |
Scheduling | {} / [] |