OneDev is a self-hosted Git platform and CI/CD engine that replaces Azure DevOps with visual pipelines, issue boards, and low-overhead MIT architecture.
Robin Shen built OneDev starting in 2012 as a lightweight Git server and automation platform designed to avoid the resource hunger of sprawling DevOps suites. Over a decade of daily dogfooding, that work grew into an integrated system spanning repository hosting, visual CI/CD, issue boards, and package registries inside a single JVM process. Today, it gives engineering teams a self-hosted alternative that runs on a budget virtual machine instead of a distributed cluster.
Azure DevOps charges monthly per-user subscription fees once teams pass five accounts, and it meters concurrency through paid parallel pipeline jobs. For growing organizations, seat licenses and runner fees steadily drive up software budgets. OneDev eliminates recurring user and concurrency fees entirely. Teams can host repositories, manage agile boards, and execute parallel builds on their own hardware without subscription overhead.
The cost advantage shifts operational duties onto your team. Azure DevOps offloads high availability, automated patching, and managed storage to Microsoft. OneDev trades that managed convenience for infrastructure ownership and a compact footprint, running comfortably on a single 2 GB server for mid-sized teams. Choosing OneDev removes per-seat bills, but you must provision compute hosts, isolate container workloads, and maintain regular database backups yourself.
.onedev-buildspec.yml file versioned in the repository.Is OneDev free for commercial use? Yes. OneDev carries an MIT open-source license with no commercial restrictions, seat limits, or paid feature tiers.
OneDev vs Azure DevOps — which should a team choose? Choose Azure DevOps if your organization relies on Microsoft-managed cloud infrastructure, commercial support contracts, and tight Azure service integration. Choose OneDev if you want a self-hosted platform that removes per-user licensing costs while operating on modest server hardware.
What are the minimum hardware requirements to self-host OneDev? OneDev runs on a Linux host with one CPU core and 2 GB of RAM for small to medium repositories, though running multiple concurrent build containers will require additional memory and CPU capacity.
OneDev is a practical choice for teams seeking an integrated development forge that keeps source code, CI/CD automation, and project management on private infrastructure without recurring license invoices. The project demonstrates active maintenance with commits and releases continuing through September 2026, and its permissive MIT terms grant complete freedom to deploy and modify the platform across private environments. Organizations without internal systems administration resources to oversee backups and host isolation, however, will likely find managed cloud alternatives worth the recurring fee.
We started this exact setup from scratch in a clean Linux machine on Nov 1, 2026 and checked it through OneDev's own API: every check passed. Recipes are re-checked regularly and removed if they stop working.
mkdir -p onedev && cd onedev
curl -fsSL https://open-source.store/tools/onedev/compose.yml -o compose.ymlcat > .env <<EOF
ADMIN_PASSWORD=$(openssl rand -hex 12)
SERVER_URL=http://localhost:6610
EOFADMIN_PASSWORD — Initial administrator password for OneDev. Generated above; keep it secret.SERVER_URL — Public URL where OneDev is reached (e.g. https://onedev.example.com); set this to your server's public URL.docker compose up -dservices:
onedev:
image: 1dev/server:17.0.2
ports:
- "6610:6610"
environment:
initial_user: admin
initial_password: ${ADMIN_PASSWORD}
initial_email: [email protected]
initial_server_url: ${SERVER_URL}
volumes:
- onedev-data:/opt/onedev
restart: always
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:6610/readyz || exit 1"]
interval: 10s
timeout: 5s
retries: 30
start_period: 30s
volumes:
onedev-data:
Go, gRPC and Protobuf done right, LLM agents in production, and the hard parts of shipping a first product.
Show that OneDev is listed as an open-source alternative. Paste into your README:
Ubuntu 24.04 · Apple container, Docker 29.1.3, Compose 2.40.3+ds1-0ubuntu1~24.04.1. Images pulled in 358 s: 1dev/server:17.0.2.
Based on the upstream setup. Official Docker server image with unattended admin setup and embedded database. Image pinned to 17.0.2; initial_server_url moved to .env; docker.sock dropped (host Docker execution for CI/CD jobs disabled; use remote agents) and Git SSH port 6611 dropped (use HTTP/HTTPS cloning).
onedev-1 | 2026-11-01T05:27:04.117471470Z at io.onedev.server.security.CorsFilter.doFilter(CorsFilter.java:47) onedev-1 | 2026-11-01T05:27:04.117472137Z at org.eclipse.jetty.ee11.servlet.FilterHolder.doFilter(FilterHolder.java:205) onedev-1 | 2026-11-01T05:27:04.117472762Z at org.eclipse.jetty.ee11.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1647) onedev-1 | 2026-11-01T05:27:04.117551137Z at io.onedev.server.jetty.DisableTraceFilter.doFilter(DisableTraceFilter.java:28) onedev-1 | 2026-11-01T05:27:04.117552970Z at org.eclipse.jetty.ee11.servlet.FilterHolder.doFilter(FilterHolder.java:205) onedev-1 | 2026-11-01T05:27:04.117553553Z at org.eclipse.jetty.ee11.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1647) onedev-1 | 2026-11-01T05:27:04.117554095Z at org.eclipse.jetty.ee11.servlet.ServletHandler$MappedServlet.handle(ServletHandler.java:1602) onedev-1 | 2026-11-01T05:27:04.117554637Z at org.eclipse.jetty.ee11.servlet.ServletChannel.dispatch(ServletChannel.java:871) onedev-1 | 2026-11-01T05:27:04.117555178Z at org.eclipse.jetty.ee11.servlet.ServletChannel.handle(ServletChannel.java:449) onedev-1 | 2026-11-01T05:27:04.117555720Z at org.eclipse.jetty.ee11.servlet.ServletHandler.handle(ServletHandler.java:469) onedev-1 | 2026-11-01T05:27:04.117556262Z at org.eclipse.jetty.ee11.servlet.SessionHandler.handle(SessionHandler.java:763) onedev-1 | 2026-11-01T05:27:04.117557053Z at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1253) onedev-1 | 2026-11-01T05:27:04.117557595Z at org.eclipse.jetty.compression.server.CompressionHandler.handle(CompressionHandler.java:343) onedev-1 | 2026-11-01T05:27:04.117558137Z at org.eclipse.jetty.server.Handler$Sequence.handle(Handler.java:859) onedev-1 | 2026-11-01T05:27:04.117558637Z at org.eclipse.jetty.server.Server.handle(Server.java:197) onedev-1 | 2026-11-01T05:27:04.117559137Z at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:804) onedev-1 | 2026-11-01T05:27:04.117559678Z at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:420) onedev-1 | 2026-11-01T05:27:04.117560220Z at org.eclipse.jetty.server.internal.HttpConnection$FillableCallback.succeeded(HttpConnection.java:1790) onedev-1 | 2026-11-01T05:27:04.117560762Z at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) onedev-1 | 2026-11-01T05:27:04.117561220Z at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:54) onedev-1 | 2026-11-01T05:27:04.117561762Z at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:492) onedev-1 | 2026-11-01T05:27:04.117562303Z at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.epcRunTask(AdaptiveExecutionStrategy.java:428) onedev-1 | 2026-11-01T05:27:04.117562887Z at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:401) onedev-1 | 2026-11-01T05:27:04.117563428Z at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:255) onedev-1 | 2026-11-01T05:27:04.117564012Z at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:204) onedev-1 | 2026-11-01T05:27:04.117564553Z at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:317) onedev-1 | 2026-11-01T05:27:04.117565220Z at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:1009) onedev-1 | 2026-11-01T05:27:04.117565762Z at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1240) onedev-1 | 2026-11-01T05:27:04.117566262Z at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1194) onedev-1 | 2026-11-01T05:27:04.117567303Z at java.base/java.lang.Thread.run(Thread.java:840) onedev-1 | 2026-11-01T05:27:04.117567803Z Caused by: org.eclipse.jetty.util.Utf8StringBuilder$Utf8IllegalArgumentException: CharacterCodingException@7d4c8d60: Invalid UTF-8 onedev-1 | 2026-11-01T05:27:04.117568387Z at org.eclipse.jetty.util.Utf8StringBuilder.errorActionReport(Utf8StringBuilder.java:145) onedev-1 | 2026-11-01T05:27:04.117569053Z at org.eclipse.jetty.util.Utf8StringBuilder.handleUnmappableCharacter(Utf8StringBuilder.java:177) onedev-1 | 2026-11-01T05:27:04.117569595Z at org.eclipse.jetty.util.Utf8StringBuilder.appendByte(Utf8StringBuilder.java:397) onedev-1 | 2026-11-01T05:27:04.117570053Z at org.eclipse.jetty.util.Utf8StringBuilder.append(Utf8StringBuilder.java:271) onedev-1 | 2026-11-01T05:27:04.117570553Z at org.eclipse.jetty.util.UrlParameterDecoder.decodeHexByteTo(UrlParameterDecoder.java:318) onedev-1 | 2026-11-01T05:27:04.117571053Z at org.eclipse.jetty.util.UrlParameterDecoder.parseChar(UrlParameterDecoder.java:236) onedev-1 | 2026-11-01T05:27:04.117571553Z at org.eclipse.jetty.util.UrlParameterDecoder.parseCompletely(UrlParameterDecoder.java:176) onedev-1 | 2026-11-01T05:27:04.117572053Z at org.eclipse.jetty.util.UrlParameterDecoder.parse(UrlParameterDecoder.java:125) onedev-1 | 2026-11-01T05:27:04.117572553Z at org.eclipse.jetty.util.UrlEncoded.decodeUtf8To(UrlEncoded.java:392) onedev-1 | 2026-11-01T05:27:04.117573012Z at org.eclipse.jetty.server.Request.extractQueryParameters(Request.java:620) onedev-1 | 2026-11-01T05:27:04.117573512Z ... 63 common frames omitted onedev-1 | 2026-11-01T05:27:04.117573970Z Caused by: CharacterCodingException@7d4c8d60: Invalid UTF-8 onedev-1 | 2026-11-01T05:27:04.117574470Z at org.eclipse.jetty.util.Utf8StringBuilder$Utf8IllegalArgumentException.<init>(Utf8StringBuilder.java:543) onedev-1 | 2026-11-01T05:27:04.117575095Z ... 73 common frames omitted onedev-1 | 2026-11-01T05:27:04.118762220Z 05:27:04 ERROR i.o.s.security.ExceptionHandleFilter - Error processing servlet request onedev-1 | 2026-11-01T05:27:04.118774137Z org.eclipse.jetty.http.HttpException$IllegalStateException: 400: Bad query onedev-1 | 2026-11-01T05:27:04.118775095Z at org.eclipse.jetty.server.Request.extractQueryParameters(Request.java:635) onedev-1 | 2026-11-01T05:27:04.118775928Z at org.eclipse.jetty.ee11.servlet.ServletApiRequest.extractQueryParameters(ServletApiRequest.java:1209) onedev-1 | 2026-11-01T05:27:04.118776595Z at org.eclipse.jetty.ee11.servlet.ServletApiRequest.getParameters(ServletApiRequest.java:1074) onedev-1 | 2026-11-01T05:27:04.118777178Z at org.eclipse.jetty.ee11.servlet.ServletApiRequest.getParameter(ServletApiRequest.java:1042) onedev-1 | 2026-11-01T05:27:04.118777720Z at jakarta.servlet.ServletRequestWrapper.getParameter(ServletRequestWrapper.java:161) onedev-1 | 2026-11-01T05:27:04.118778387Z at io.onedev.server.git.GitLfsFilter.doFilter(GitLfsFilter.java:179) onedev-1 | 2026-11-01T05:27:04.118778970Z at org.eclipse.jetty.ee11.servlet.FilterHolder.doFilter(FilterHolder.java:205) onedev-1 | 2026-11-01T05:27:04.118782845Z at org.eclipse.jetty.ee11.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1647) onedev-1 | 2026-11-01T05:27:04.118785595Z at io.onedev.server.git.GitFilter.doFilter(GitFilter.java:394) onedev-1 | 2026-11-01T05:27:04.118786220Z at org.eclipse.jetty.ee11.servlet.FilterHolder.doFilter(FilterHolder.java:205) onedev-1 | 2026-11-01T05:27:04.118786803Z at org.eclipse.jetty.ee11.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1647) onedev-1 | 2026-11-01T05:27:04.118787387Z at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:65) onedev-1 | 2026-11-01T05:27:04.118787970Z at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:109) onedev-1 | 2026-11-01T05:27:04.118788512Z at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:138) onedev-1 | 2026-11-01T05:27:04.118789095Z at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:156) onedev-1 | 2026-11-01T05:27:04.118789637Z at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:70) onedev-1 | 2026-11-01T05:27:04.118790178Z at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:109) onedev-1 | 2026-11-01T05:27:04.118790720Z at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:138) onedev-1 | 2026-11-01T05:27:04.118791262Z at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:156) onedev-1 | 2026-11-01T05:27:04.118791928Z at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:70) onedev-1 | 2026-11-01T05:27:04.118792470Z at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:109) onedev-1 | 2026-11-01T05:27:04.118793262Z at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:138) onedev-1 | 2026-11-01T05:27:04.118793762Z at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:156) onedev-1 | 2026-11-01T05:27:04.118794303Z at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:70) onedev-1 | 2026-11-01T05:27:04.118794803Z at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:463) onedev-1 | 2026-11-01T05:27:04.118795345Z at io.onedev.server.security.DefaultShiroFilter.executeChain(DefaultShiroFilter.java:25) onedev-1 | 2026-11-01T05:27:04.118795845Z at org.apache.shiro.web.servlet.AbstractShiroFilter.lambda$doFilterInternal$0(AbstractShiroFilter.java:377) onedev-1 | 2026-11-01T05:27:04.118796470Z at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:102) onedev-1 | 2026-11-01T05:27:04.118797012Z at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:95) onedev-1 | 2026-11-01T05:27:04.118797512Z at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:388) onedev-1 | 2026-11-01T05:27:04.118798053Z at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:375) onedev-1 | 2026-11-01T05:27:04.118798595Z at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:156) onedev-1 | 2026-11-01T05:27:04.118799095Z at org.eclipse.jetty.ee11.servlet.FilterHolder.doFilter(FilterHolder.java:205) onedev-1 | 2026-11-01T05:27:04.118800137Z at org.eclipse.jetty.ee11.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1647) onedev-1 | 2026-11-01T05:27:04.118800678Z at io.onedev.server.security.CorsFilter.doFilter(CorsFilter.java:47) onedev-1 | 2026-11-01T05:27:04.118801178Z at org.eclipse.jetty.ee11.servlet.FilterHolder.doFilter(FilterHolder.java:205) onedev-1 | 2026-11-01T05:27:04.118801678Z at org.eclipse.jetty.ee11.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1647) onedev-1 | 2026-11-01T05:27:04.119217887Z at io.onedev.server.jetty.DisableTraceFilter.doFilter(DisableTraceFilter.java:28) onedev-1 | 2026-11-01T05:27:04.119220387Z at org.eclipse.jetty.ee11.servlet.FilterHolder.doFilter(FilterHolder.java:205) onedev-1 | 2026-11-01T05:27:04.119221053Z at org.eclipse.jetty.ee11.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1647) onedev-1 | 2026-11-01T05:27:04.119221637Z at org.eclipse.jetty.ee11.servlet.ServletHandler$MappedServlet.handle(ServletHandler.java:1602) onedev-1 | 2026-11-01T05:27:04.119222220Z at org.eclipse.jetty.ee11.servlet.ServletChannel.dispatch(ServletChannel.java:871) onedev-1 | 2026-11-01T05:27:04.119222803Z at org.eclipse.jetty.ee11.servlet.ServletChannel.handle(ServletChannel.java:449) onedev-1 | 2026-11-01T05:27:04.119223345Z at org.eclipse.jetty.ee11.servlet.ServletHandler.handle(ServletHandler.java:469) onedev-1 | 2026-11-01T05:27:04.119223887Z at org.eclipse.jetty.ee11.servlet.SessionHandler.handle(SessionHandler.java:763) onedev-1 | 2026-11-01T05:27:04.119224970Z at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1253) onedev-1 | 2026-11-01T05:27:04.119225512Z at org.eclipse.jetty.compression.server.CompressionHandler.handle(CompressionHandler.java:343) onedev-1 | 2026-11-01T05:27:04.119226095Z at org.eclipse.jetty.server.Handler$Sequence.handle(Handler.java:859) onedev-1 | 2026-11-01T05:27:04.119226595Z at org.eclipse.jetty.server.Server.handle(Server.java:197) onedev-1 | 2026-11-01T05:27:04.119227137Z at org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:804) onedev-1 | 2026-11-01T05:27:04.119227720Z at org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:420) onedev-1 | 2026-11-01T05:27:04.119228262Z at org.eclipse.jetty.server.internal.HttpConnection$FillableCallback.succeeded(HttpConnection.java:1790) onedev-1 | 2026-11-01T05:27:04.119228845Z at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) onedev-1 | 2026-11-01T05:27:04.119229345Z at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:54) onedev-1 | 2026-11-01T05:27:04.119229887Z at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:1009) onedev-1 | 2026-11-01T05:27:04.119230428Z at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1240) onedev-1 | 2026-11-01T05:27:04.119230970Z at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1194) onedev-1 | 2026-11-01T05:27:04.119231512Z at java.base/java.lang.Thread.run(Thread.java:840) onedev-1 | 2026-11-01T05:27:04.119232053Z Caused by: org.eclipse.jetty.util.Utf8StringBuilder$Utf8IllegalArgumentException: CharacterCodingException@e22a1b3: Invalid UTF-8 onedev-1 | 2026-11-01T05:27:04.119236428Z at org.eclipse.jetty.util.Utf8StringBuilder.errorActionReport(Utf8StringBuilder.java:145) onedev-1 | 2026-11-01T05:27:04.119237053Z at org.eclipse.jetty.util.Utf8StringBuilder.handleUnmappableCharacter(Utf8StringBuilder.java:177) onedev-1 | 2026-11-01T05:27:04.119237637Z at org.eclipse.jetty.util.Utf8StringBuilder.appendByte(Utf8StringBuilder.java:397) onedev-1 | 2026-11-01T05:27:04.119238178Z at org.eclipse.jetty.util.Utf8StringBuilder.append(Utf8StringBuilder.java:271) onedev-1 | 2026-11-01T05:27:04.119238678Z at org.eclipse.jetty.util.UrlParameterDecoder.decodeHexByteTo(UrlParameterDecoder.java:318) onedev-1 | 2026-11-01T05:27:04.119239220Z at org.eclipse.jetty.util.UrlParameterDecoder.parseChar(UrlParameterDecoder.java:236) onedev-1 | 2026-11-01T05:27:04.119239762Z at org.eclipse.jetty.util.UrlParameterDecoder.parseCompletely(UrlParameterDecoder.java:176) onedev-1 | 2026-11-01T05:27:04.119240262Z at org.eclipse.jetty.util.UrlParameterDecoder.parse(UrlParameterDecoder.java:125) onedev-1 | 2026-11-01T05:27:04.119240803Z at org.eclipse.jetty.util.UrlEncoded.decodeUtf8To(UrlEncoded.java:392) onedev-1 | 2026-11-01T05:27:04.119241470Z at org.eclipse.jetty.server.Request.extractQueryParameters(Request.java:620) onedev-1 | 2026-11-01T05:27:04.119242053Z ... 57 common frames omitted onedev-1 | 2026-11-01T05:27:04.119242595Z Caused by: CharacterCodingException@e22a1b3: Invalid UTF-8 onedev-1 | 2026-11-01T05:27:04.119243262Z at org.eclipse.jetty.util.Utf8StringBuilder$Utf8IllegalArgumentException.<init>(Utf8StringBuilder.java:543) onedev-1 | 2026-11-01T05:27:04.119243928Z ... 67 common frames omitted