Self-hosted service
dotnetcore/AgileConfig avatar
dotnetcore/AgileConfig

AgileConfig pushes configuration changes over websockets, and any node can edit them

基于.NET Core开发的轻量级分布式配置中心 / .NET Core lightweight configuration server

1,625 stars322 forksC#MIT

At a glance

What is it?
AgileConfig is a .NET Core configuration server that clients connect to in real time over websockets, with multiple environments, version rollback, OIDC/SSO and six database providers. The admin console is not a separate component, which is the design decision worth understanding first.
Who is it for?
AgileConfig fits .NET shops that want configuration changes applied without a redeploy, with several environments and a rollback history, and that can accept a self hosted server on port 5000 backed by one of the six listed databases. It does not fit a polyglot estate on the .NET client alone, since the official clients are community repositories for Java, Go, Python, Node and Rust and you will be depending on their maintenance.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 34 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Client, node, database: three pieces and a websocket between them

The architecture section names three parts.

The client is a .NET client that reads configurations from a server node. The node is just a .NET Core app, and clients connect to it in real time over websocket. The database holds the configuration history.

The sentence that decides the topology is this: any node can be an admin console to manage configurations. There is no separate management service to deploy, secure and keep alive. In the compose file shipped with the repository, the difference between the admin instance and the others is a single environment variable. agile_config_admin has adminConsole=true, while agile_config_node1 and agile_config_node2 set cluster=true, and all three point at the same MySQL instance with the same connection string.

That design also sets the failure model. If every node can become the console, then any node you expose can be administered, and the difference between a management node and a plain node is configuration rather than a different binary or a separate port.

The database layer is provider based. The README lists sqlserver for SqlServer, mysql for MySql, sqlite for Sqlite, npgsql for PostgreSql, oracle for Oracle and mongodb for Mongodb, and states that AgileConfig supports most popular databases.

Beyond configuration, the feature list includes a restful api, client fault tolerance, version management and easy rollback, multiple environments, support for OIDC/SSO, support for OpenTelemetry, role based user permission control, and the note that it can also be used as a simple service register center.

Running a node in Docker, with sqlite as the zero dependency path

The README's Docker run is the shortest path to a working instance:

shell
sudo docker run \
--name agile_config \
-e TZ=Asia/Shanghai \
-e adminConsole=true \
-e db__provider=sqlite \
-e db__conn="Data Source=agile_config.db" \
-p 5000:5000 \
-v /etc/localtime:/etc/localtime \
#-v /your_host_dir:/app/db \
-d kklldog/agile_config:latest

The double underscore in db__provider and db__conn is the ASP.NET Core environment variable convention for nested configuration, so the same keys work whether they come from environment variables or from appsettings.json. The sqlite connection string, Data Source=agile_config.db, needs no server, which is what makes this the evaluation setup. The commented volume line is where you would persist the database file once the container's writable layer is no longer a reasonable place for it.

After the container starts, the console is at http://localhost:5000. A public demo is linked in the README with the credentials admin/123456, which is a reasonable thing to try before you deploy your own.

The compose file in the repository shows the clustered version of the same thing: three services, ports 15000, 15001 and 15002 mapped to 5000, a net0 network, seccomp=unconfined on each service, and node1 and node2 declaring depends_on the admin service. The security_opt line is there for a reason the compose file does not explain, so treat it as something to verify in your own environment rather than copy without thinking.

IIS deployment is the other option the feature list names, alongside Docker.

Client wiring: a section in appsettings and one call on the host builder

The client library comes from NuGet:

code
Install-Package AgileConfig.Client

Configuration is a block in appsettings.json, where the node list is a comma separated string:

json
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",

  //agile_config
  "AgileConfig": {
    "appId": "app",
    "secret": "xxx",
    "nodes": "http://localhost:5000,http://localhost:5001",
    "name": "client_name",
    "tag": "tag1",
    "env": "dev"
  }
}

Two of those fields carry more weight than the rest. env selects the environment, which is how one configuration centre serves several environments. nodes is a list, so a client can fail over between servers, and client fault tolerance is a listed feature.

Registering the client is one method call on the host builder:

csharp
   public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseAgileConfig()
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

Because it plugs into the standard configuration pipeline, application code does not change to read from it. The README is explicit that you can still use IConfiguration or the IOptions pattern to get the specific configuration value, and both are shown being injected into a controller alongside each other.

Three ways to read a value, from idiomatic to direct

Reading is where the design pays off, because nothing about your existing code has to change.

The idiomatic path is IConfiguration, or IOptions<T> for a bound options class, both injected through the constructor. That is the pattern most .NET code already uses, so a value moved into AgileConfig is read the same way as one in appsettings.json.

The second path is the IConfigClient interface, which lets you index keys with the familiar colon notation and enumerate everything:

csharp
var userId = _configClient["userId"];
var dbConn = _configClient["db:connection"];

foreach (var item in _configClient.Data)
{
    Console.WriteLine($"{item.Key} = {item.Value}");
}

The db:connection form is the same hierarchical key syntax IConfiguration uses, which keeps a migration between the two from becoming a rewrite.

The third is a singleton with no injection at all:

csharp
var userid = ConfigClient.Instance["userid"]

That is the option to be careful with. It is convenient in scripts and console samples, and it is untestable in a way the other two are not, because the dependency is resolved by a global rather than by the container.

Sample projects cover MVC, WPF and console, so the range of host types the client supports is visible in the linked repositories rather than only asserted.

The client ecosystem is the adoption risk

The README lists clients maintained outside this repository, and none of them are under the dotnetcore organisation. The .NET client is AgileConfig_Client, and the Java, Go, Python, Node, Rust and Blazor clients are separate community projects with their own owners, including two competing Python clients and two Go clients.

That is worth stating plainly. The server is the product, and the polyglot story depends on projects this organisation does not control, in some cases with overlapping options for the same language. If your services are not .NET, you are choosing among community clients rather than a supported surface.

The alternative for a mixed estate is to keep the .NET client as the supported path and expose configuration to other languages through something else, such as reading the REST API the feature list mentions, or through environment variables populated at deploy time by whatever already handles your releases.

The restful API is documented on the project wiki, along with a general Q&A wiki, so the interface surface for non-.NET consumers is at least written down.

Where it is the wrong choice

The first wrong case is a deployment that has no long lived service. A static site or a job that starts, reads configuration and exits gains nothing from a push-based configuration server, and now has a network dependency it did not have.

The second is secrets. The client block has an appId and a secret in it, and the compose file shows database credentials inline in the environment of each service. Neither is a criticism unique to this project, but a configuration centre is a tempting place to put secrets, and the README does not document a secret store, encryption at rest, or a masking policy for the admin console.

The third is the any node can be an admin console model combined with an exposed console. The demo credentials in the README, admin/123456, are a reminder that a configuration console is a high value target, and the feature list says role based permissions and OIDC/SSO are supported rather than required.

The fourth is expecting it to be a full service registry. It can also be used as a simple service register center, and the word simple is doing real work there.

Build, release cadence and licence

The Dockerfile shows a multi-stage build on .NET 10 with a separate Node 20 stage for the React admin UI at src/AgileConfig.Server.UI/react-ui-antd, installed with npm ci and built with npm run build. Both the SDK stage and the UI stage run on the native build platform with --platform=$BUILDPLATFORM and cross compile to the target, with the comment explaining that this avoids the slowdown of QEMU emulation. The runtime identifier is derived from TARGETARCH, supporting amd64 and arm64 and failing on anything else.

The base runtime image is mcr.microsoft.com/dotnet/aspnet:10.0.0 with WORKDIR /app, EXPOSE 5000, and an early step that rewrites the OpenSSL configuration to lower the security level and relax the minimum TLS protocol, which is a compatibility concession rather than a hardening step and worth noticing before you run it next to anything sensitive.

The repository has a solution file at the root, src/ and test/ directories, a Dockerfile, a docker-compose.yml, a .gitmodules file indicating at least one vendored submodule, docs/, a CHANGELOG.md and three READMEs in English, Chinese and the bilingual default. Tests are configured through coverlet.runsettings, and CI workflows are named master-ci.yml and release-xxx.yml.

Licence is MIT in License.txt. The last push was on 2026-09-19, the repository is not archived, and the releases show steady maintenance: v-1.13.1 on 2026-08-18, v-1.13.2 on 2026-08-28 and v-1.13.3 on 2026-09-19.

Editorial conclusion

AgileConfig fits .NET shops that want configuration changes applied without a redeploy, with several environments and a rollback history, and that can accept a self hosted server on port 5000 backed by one of the six listed databases. It does not fit a polyglot estate on the .NET client alone, since the official clients are community repositories for Java, Go, Python, Node and Rust and you will be depending on their maintenance. Verify first which database provider you will use, since the connection string shape differs per provider, and whether your identity provider can speak OIDC, because the authentication story changes what the admin console is open to.

Frequently asked questions

How do I run AgileConfig with Docker?

Run kklldog/agile_config:latest with adminConsole=true, db__provider=sqlite and db__conn="Data Source=agile_config.db", publishing port 5000, then open http://localhost:5000. The compose file in the repository shows the same image in a three node cluster against MySQL.

Which databases does AgileConfig support?

The dbprovider values listed are sqlserver for SqlServer, mysql for MySql, sqlite for Sqlite, npgsql for PostgreSql, oracle for Oracle and mongodb for Mongodb.

How does the AgileConfig .NET client read configuration?

Install the client with Install-Package AgileConfig.Client, add an AgileConfig section to appsettings.json with appId, secret, nodes, name, tag and env, and call UseAgileConfig() on the host builder. Values are then read through IConfiguration, IOptions or the IConfigClient interface.

Does AgileConfig work outside .NET?

There is no official non-.NET client in this repository. The README lists community clients for Java, Go, Python, Node and Rust, plus a Blazor UI project, each maintained separately.

How does AgileConfig push configuration changes?

Clients connect to a node in real time over websocket, and the feature list states that configuration changes take effect in real time, with client fault tolerance and multiple nodes listed as a comma separated nodes value in the client configuration.

Official sources

  1. dotnetcore/AgileConfig on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dotnetcore-agileconfig.svg)](https://hysenlabs.com/projects/dotnetcore-agileconfig)

Was this useful?