DevOps & monitoring

Runtime TLS certificate acquisition in Go eliminates restarts

The Dushed team details a Go-based approach to acquiring and renewing Let's Encrypt certificates at runtime, avoiding external tools and service restarts.

Server rack with a green shield and floating clocks representing runtime certificate management
Illustration created for this article

On October 16, 2026, the developers behind Dushed, a knowledge management web application, published a technical deep-dive on managing Transport Layer Security (TLS) certificates directly within their Go application. Instead of relying on external automation tools, they implemented a system that procures and renews certificates from Let's Encrypt during runtime, eliminating the need for process restarts after renewal.

What happened

Traditionally, web servers obtain TLS certificates using out-of-band tools like Certbot or other ACME clients. These tools run separately from the web server, often requiring configuration changes and service restarts to load new certificates. The Dushed team sought to simplify this operational overhead for their self-hosted application, which aggregates notes, bookmarks, files, and feeds into a single library. They aimed to remove the dependency on external renewal processes that would otherwise force them to restart the Dushed service every time a certificate was updated.

To achieve this, the developers leveraged specific features of the Go standard library. In a typical Go HTTPS server setup, the ListenAndServeTLS function expects file paths to existing certificate and key files. However, the http.Server struct also accepts an optional TLSConfig field. This configuration object includes a GetCertificate function field, which allows the server to dynamically retrieve a certificate based on the client's hello message. By injecting custom logic into this function, the application can request or renew certificates on the fly without stopping the server.

The implementation relies on the golang.org/x/crypto/acme/autocert package rather than a custom-built ACME client. This package handles the communication with Let's Encrypt, including the challenge-response verification required to prove domain ownership. The getCert function checks for valid certificates in memory or on disk and triggers a renewal if necessary. This approach also supports advanced features like Server Name Indication (SNI) based certificate selection and preemptive renewal before expiration.

Key details

  • The solution is built using the Go programming language and the golang.org/x/crypto/acme/autocert library.
  • Certificate procurement and renewal happen at runtime via the GetCertificate callback in tls.Config.
  • The system avoids external tools like Certbot, removing the need for separate maintenance processes.
  • Certificates are persisted to both disk and memory to ensure availability across deployments.
  • Zero-downtime deployments are supported by using SO_REUSEPORT and SO_REUSEADDR socket options.
  • The method allows for dynamic certificate serving based on SNI and proactive renewal strategies.

Background

TLS is the protocol that encrypts data between a user's browser and a web server, ensuring privacy and integrity. To establish a secure connection, the server must present a digital certificate issued by a trusted Certificate Authority (CA). Let's Encrypt is a widely used CA that provides free, automated certificates using the ACME protocol. Automation is critical because these certificates typically expire every 90 days.

In many self-hosted environments, administrators use tools like Certbot to handle this automation. These tools usually write new certificate files to disk and then signal the web server to reload its configuration. While effective, this adds complexity to the deployment pipeline and introduces a brief window where the service might be unavailable or misconfigured during restarts. Integrating this logic directly into the application code shifts the responsibility from the operating system level to the application level, offering tighter control over the lifecycle of the security credentials.

Why it matters

For teams running self-hosted software, reducing operational complexity is a primary goal. Managing separate cron jobs, systemd timers, or container sidecars for certificate renewal adds friction to infrastructure management. By embedding the ACME client directly into the application, developers can deploy a single binary or container that handles its own security lifecycle. This reduces the risk of configuration drift between the web server settings and the external renewal tool.

Furthermore, the ability to renew certificates without restarting the service enhances reliability. In high-availability environments, even brief restarts can cause connection drops for active users. The technique described by the Dushed team ensures that the server remains responsive throughout the renewal process. It also simplifies zero-downtime deployments, as the new and old processes can share the same port and access the same cached certificates during the transition period.

What you can do

  • Evaluate if your Go-based services can benefit from embedded ACME clients to reduce external dependencies.
  • Review your current certificate renewal workflow to identify unnecessary service restarts or manual steps.
  • Implement GetCertificate callbacks in your Go servers to enable dynamic certificate loading.
  • Use SO_REUSEPORT and SO_REUSEADDR to support graceful reloads and zero-downtime deployments.
  • Ensure certificates are persisted to disk as well as memory to survive application crashes or restarts.
  • Monitor certificate expiration dates independently to catch failures in automated renewal logic.

More news

All news