diff --git a/modules/caddypki/acmeserver/acmeserver.go b/modules/caddypki/acmeserver/acmeserver.go
index 42d8cc498..388aa529e 100644
--- a/modules/caddypki/acmeserver/acmeserver.go
+++ b/modules/caddypki/acmeserver/acmeserver.go
@@ -102,9 +102,9 @@ func (ash *Handler) Provision(ctx caddy.Context) error {
 		return err
 	}
 	pkiApp := appModule.(*caddypki.PKI)
-	ca, ok := pkiApp.CAs[ash.CA]
-	if !ok {
-		return fmt.Errorf("no certificate authority configured with id: %s", ash.CA)
+	ca, err := pkiApp.GetCA(ash.CA, &ctx)
+	if err != nil {
+		return err
 	}
 
 	database, err := ash.openDatabase()
diff --git a/modules/caddypki/pki.go b/modules/caddypki/pki.go
index 012eaafdb..f391fdaf5 100644
--- a/modules/caddypki/pki.go
+++ b/modules/caddypki/pki.go
@@ -34,6 +34,8 @@ func init() {
 type PKI struct {
 	// The certificate authorities to manage. Each CA is keyed by an
 	// ID that is used to uniquely identify it from other CAs.
+	// At runtime, the GetCA() method should be used instead to ensure
+	// the default CA is provisioned if it hadn't already been.
 	// The default CA ID is "local".
 	CAs map[string]*CA `json:"certificate_authorities,omitempty"`
 
@@ -54,16 +56,6 @@ func (p *PKI) Provision(ctx caddy.Context) error {
 	p.ctx = ctx
 	p.log = ctx.Logger(p)
 
-	// if this app is initialized at all, ensure there's at
-	// least a default CA that can be used: the standard CA
-	// which is used implicitly for signing local-use certs
-	if p.CAs == nil {
-		p.CAs = make(map[string]*CA)
-	}
-	if _, ok := p.CAs[DefaultCAID]; !ok {
-		p.CAs[DefaultCAID] = new(CA)
-	}
-
 	for caID, ca := range p.CAs {
 		err := ca.Provision(ctx, caID, p.log)
 		if err != nil {
@@ -71,9 +63,29 @@ func (p *PKI) Provision(ctx caddy.Context) error {
 		}
 	}
 
+	// if this app is initialized at all, ensure there's at
+	// least a default CA that can be used: the standard CA
+	// which is used implicitly for signing local-use certs
+	if len(p.CAs) == 0 {
+		err := p.ProvisionDefaultCA(ctx)
+		if err != nil {
+			return fmt.Errorf("provisioning CA '%s': %v", DefaultCAID, err)
+		}
+	}
+
 	return nil
 }
 
+// ProvisionDefaultCA sets up the default CA.
+func (p *PKI) ProvisionDefaultCA(ctx caddy.Context) error {
+	if p.CAs == nil {
+		p.CAs = make(map[string]*CA)
+	}
+
+	p.CAs[DefaultCAID] = new(CA)
+	return p.CAs[DefaultCAID].Provision(ctx, DefaultCAID, p.log)
+}
+
 // Start starts the PKI app.
 func (p *PKI) Start() error {
 	// install roots to trust store, if not disabled
@@ -107,6 +119,32 @@ func (p *PKI) Stop() error {
 	return nil
 }
 
+// GetCA retrieves a CA by ID. If the ID is the default
+// CA ID, and it hasn't been provisioned yet, it will
+// be provisioned. The context must be passed if this
+// is called from a module's Provision().
+func (p *PKI) GetCA(id string, ctx *caddy.Context) (*CA, error) {
+	ca, ok := p.CAs[id]
+	if !ok {
+		// for anything other than the default CA ID, error out if it wasn't configured
+		if id != DefaultCAID {
+			return nil, fmt.Errorf("no certificate authority configured with id: %s", id)
+		}
+
+		// for the default CA ID, provision it, because we want it to "just work"
+		if ctx == nil {
+			return nil, fmt.Errorf("cannot provision default CA without the context")
+		}
+		err := p.ProvisionDefaultCA(*ctx)
+		if err != nil {
+			return nil, fmt.Errorf("failed to provision default CA: %s", err)
+		}
+		ca = p.CAs[id]
+	}
+
+	return ca, nil
+}
+
 // Interface guards
 var (
 	_ caddy.Provisioner = (*PKI)(nil)
diff --git a/modules/caddytls/internalissuer.go b/modules/caddytls/internalissuer.go
index 7a25f6d3b..d9b6117d4 100644
--- a/modules/caddytls/internalissuer.go
+++ b/modules/caddytls/internalissuer.go
@@ -19,7 +19,6 @@ import (
 	"context"
 	"crypto/x509"
 	"encoding/pem"
-	"fmt"
 	"time"
 
 	"github.com/caddyserver/caddy/v2"
@@ -68,18 +67,20 @@ func (InternalIssuer) CaddyModule() caddy.ModuleInfo {
 func (iss *InternalIssuer) Provision(ctx caddy.Context) error {
 	iss.logger = ctx.Logger(iss)
 
+	// set some defaults
+	if iss.CA == "" {
+		iss.CA = caddypki.DefaultCAID
+	}
+
 	// get a reference to the configured CA
 	appModule, err := ctx.App("pki")
 	if err != nil {
 		return err
 	}
 	pkiApp := appModule.(*caddypki.PKI)
-	if iss.CA == "" {
-		iss.CA = caddypki.DefaultCAID
-	}
-	ca, ok := pkiApp.CAs[iss.CA]
-	if !ok {
-		return fmt.Errorf("no certificate authority configured with id: %s", iss.CA)
+	ca, err := pkiApp.GetCA(iss.CA, &ctx)
+	if err != nil {
+		return err
 	}
 	iss.ca = ca