TechnitiumSoftware/DnsServer · error · DnsWebServiceException

The DNS web service is already running.

Error message

The DNS web service is already running.

What it means

Thrown by DnsWebService.StartAsync() as a re-entrancy guard: it checks the private _isRunning flag (set true only after a fully successful start at line 2956, cleared by StopAsync()/DisposeAsync at line 2986) and refuses to start a second concurrent instance. It is a DnsWebServiceException (custom Exception subclass), not an InvalidOperationException, so callers catching generic exceptions still see it. The library throws it because StartAsync initializes DNS/DHCP servers, binds TCP/UDP listeners, and rewrites config — running it twice would double-bind ports and corrupt shared managers.

Source

Thrown at DnsServerCore/DnsWebService.cs:2883

        {
            _authManager.RemoveAllPermissions(PermissionSection.Zones, e.ZoneInfo.Name);
            _authManager.SaveConfigFile();

            //delete cache for this zone to allow rebuilding cache data without using the current zone
            _dnsServer.CacheZoneManager.DeleteZone(e.ZoneInfo.Name);
        }

        #endregion

        #region public

        public async Task StartAsync(bool throwIfBindFails = false)
        {
            if (_disposed)
                ObjectDisposedException.ThrowIf(_disposed, this);

            if (_isRunning)
                throw new DnsWebServiceException("The DNS web service is already running.");

            try
            {
                //init dns server
                _dnsServer = new DnsServer(_configFolder, Path.Combine(_appFolder, "dohwww"), _log);

                //init dhcp server
                _dhcpServer = new DhcpServer(Path.Combine(_configFolder, "scopes"), _log);
                _dhcpServer.DnsServer = _dnsServer;
                _dhcpServer.AuthManager = _authManager;

                //load web service config file
                LoadConfigFile();

                //load dns config file
                _dnsServer.LoadConfigFile();

                //load all dns applications

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Await StopAsync() (or DisposeAsync) on the existing instance and wait for it to complete before calling StartAsync() again — StartAsync only succeeds from the stopped state.
  2. If restarting, replace the instance entirely: dispose the old DnsWebService, create a new one, then StartAsync. This avoids any half-stopped state.
  3. Ensure StartAsync is driven from a single owner (one IHostedService / one background service) and guard the call site with your own _started flag so retry/idempotency logic never double-invokes it.
  4. If a previous StartAsync threw, _isRunning stays false, so retrying StartAsync is safe — but verify via logs that the prior attempt actually failed rather than partially bound ports before retrying.

Example fix

// before
await webService.StartAsync();
// ... later, 'reload' handler mistakenly calls it again
await webService.StartAsync(); // throws DnsWebServiceException

// after
if (webService is not null)
    await webService.StopAsync();
await webService.StartAsync(throwIfBindFails: true);
Defensive patterns

Strategy: try-catch

Validate before calling

// _isRunning is private with no public accessor, so track ownership at the call site.
// The caller must keep its own start/stop bookkeeping.
private int _started; // 0 = stopped, 1 = running

public async Task StartOnceAsync(DnsWebService svc, bool throwIfBindFails = false)
{
    if (Interlocked.CompareExchange(ref _started, 1, 0) != 0)
        return; // already started; avoid the double-start throw
    try
    {
        await svc.StartAsync(throwIfBindFails);
    }
    catch
    {
        Volatile.Write(ref _started, 0); // start failed, _isRunning stayed false
        throw;
    }
}

public async Task StopOnceAsync(DnsWebService svc)
{
    if (Interlocked.CompareExchange(ref _started, 0, 1) != 1)
        return;
    await svc.StopAsync();
}

Type guard

// No public IsRunning exists; mirror the library's own state with a single-owner flag.
bool IsServiceRunning(DnsWebService svc, ref int startedFlag)
    => Volatile.Read(ref startedFlag) == 1 && svc is not null;

Try / catch

try
{
    await webService.StartAsync(throwIfBindFails: true);
}
catch (DnsWebServiceException ex) when (ex.Message.Contains("already running"))
{
    // Recover by stopping first, then starting once.
    await webService.StopAsync();
    await webService.StartAsync(throwIfBindFails: true);
}
// Do NOT swallow other DnsWebServiceException variants (bind failures, etc.) —
// rethrow them so the caller knows the service is not up.

Prevention

When it happens

Trigger: Calling webService.StartAsync() a second time on the same DnsWebService instance without an intervening await StopAsync(). This happens in restart loops that call StartAsync again before the previous start finished, in DI/hosted-service code where StartAsync is invoked from two paths (e.g. a health-check re-trigger and a startup hook), or when the caller assumes a failed start left the service stopped (it does — _isRunning stays false on failure — but the caller retried after a *successful* start).

Common situations: A generic-host IHostedService wrapper whose StartAsync is invoked by the host and then again by application code; a 'reload settings' feature that re-runs StartAsync instead of Stop+Start; container orchestrator (k8s liveness) sidecars issuing restart commands; multiple DnsWebService instances pointing at the same config folder where one is already bound to ports 5380/53443.

Related errors


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/dbf45e6d76bad4e5. Report an issue: GitHub.