diff --git a/acme.go b/acme.go
index 51eccf9d94251b2aa484b1b38bba53f43338c20e..4cb17897872ee86b4e88c46a703b91bf7f61bc9c 100644
--- a/acme.go
+++ b/acme.go
@@ -13,7 +13,6 @@ import (
 	"io/ioutil"
 	"log"
 	"net/http"
-	"path/filepath"
 
 	"golang.org/x/crypto/acme"
 )
@@ -46,6 +45,9 @@ func NewACME(config *Config) (*ACME, error) {
 	if config.Email == "" {
 		return nil, errors.New("configuration parameter 'email' is unset")
 	}
+	if config.AccountKeyPath == "" {
+		return nil, errors.New("configuration parameter 'account_key_path' is unset")
+	}
 
 	var h http.Handler
 	v := make(map[string]validator)
@@ -77,7 +79,7 @@ func NewACME(config *Config) (*ACME, error) {
 
 	return &ACME{
 		email:                config.Email,
-		accountKeyPath:       filepath.Join(config.Dir, "account.key"),
+		accountKeyPath:       config.AccountKeyPath,
 		directoryURL:         directoryURL,
 		handler:              h,
 		validators:           v,
diff --git a/cmd/acmeserver/acmeserver.go b/cmd/acmeserver/acmeserver.go
index 31e4d0c8aaefc1f5d0fdef34dc46bdd84ed4d77c..2da3ef696fafce22353eb3f66143fc84563dd6af 100644
--- a/cmd/acmeserver/acmeserver.go
+++ b/cmd/acmeserver/acmeserver.go
@@ -17,7 +17,7 @@ import (
 
 var (
 	addr       = flag.String("addr", ":2780", "tcp `address` to listen on")
-	configFile = flag.String("config", "/etc/acmeserver/config.yml", "configuration `file`")
+	configFile = flag.String("config", "/etc/acme/config.yml", "configuration `file`")
 )
 
 // Config ties together the acmeserver Config and the standard
diff --git a/config.go b/config.go
index b009de492cfacac1ff3b3d691e50ef71ca375800..ebbc87e2c476b54b9870c521d09a68d8e2298f85 100644
--- a/config.go
+++ b/config.go
@@ -14,19 +14,28 @@ import (
 
 // Config holds the configuration for an acmeserver instance.
 //
+// The reason for supporting multiple config_dirs is to allow
+// integration with third-party automation systems: in some cases,
+// automation tools need control of an entire directory in order to
+// safely delete entries that no longer exist.
+//
 // nolint: maligned
 type Config struct {
-	Addr string `yaml:"addr"`
-
 	Testing              bool   `yaml:"testing"`
 	DirectoryURL         string `yaml:"directory_url"`
 	DefaultChallengeType string `yaml:"default_challenge"`
 	UseRSA               bool   `yaml:"use_rsa"`
 	RenewalDays          int    `yaml:"renewal_days"`
-	Email                string `yaml:"email"`
 
-	Dir    string                    `yaml:"cert_dir"`
-	ReplDS *clientutil.BackendConfig `yaml:"replds"`
+	AccountKeyPath string `yaml:"account_key_path"`
+	Email          string `yaml:"email"`
+
+	Dirs []string `yaml:"config_dirs"`
+
+	Output struct {
+		Path   string                    `yaml:"path"`
+		ReplDS *clientutil.BackendConfig `yaml:"replds"`
+	} `yaml:"output"`
 
 	HTTP struct {
 		Enabled bool `yaml:"enabled"`
@@ -79,12 +88,11 @@ func readCertConfigs(path string) ([]*certConfig, error) {
 	return cc, nil
 }
 
-func readCertConfigsFromDir(dir string) ([]*certConfig, error) {
+func readCertConfigsFromDir(dir string, certs []*certConfig) ([]*certConfig, error) {
 	files, err := filepath.Glob(filepath.Join(dir, "*.yml"))
 	if err != nil {
 		return nil, err
 	}
-	var out []*certConfig
 	for _, f := range files {
 		cc, err := readCertConfigs(f)
 		if err != nil {
@@ -97,8 +105,20 @@ func readCertConfigsFromDir(dir string) ([]*certConfig, error) {
 				log.Printf("configuration error in %s: %v", f, err)
 				continue
 			}
-			out = append(out, c)
+			certs = append(certs, c)
+		}
+	}
+	return certs, nil
+}
+
+func readCertConfigsFromDirs(dirs []string) ([]*certConfig, error) {
+	var out []*certConfig
+	for _, dir := range dirs {
+		certs, err := readCertConfigsFromDir(dir, out)
+		if err != nil {
+			return nil, err
 		}
+		out = certs
 	}
 	return out, nil
 }
diff --git a/manager.go b/manager.go
index a2f099edec7c1628d46cfb8f2306d5a40573b181..a68455506fcd35a89a9797c877e30a7d02b5ce69 100644
--- a/manager.go
+++ b/manager.go
@@ -13,7 +13,6 @@ import (
 	"errors"
 	"io"
 	"log"
-	"path/filepath"
 	"sync"
 	"time"
 
@@ -75,7 +74,7 @@ type CertGenerator interface {
 // Manager periodically renews certificates before they expire, and
 // responds to http-01 validation requests.
 type Manager struct {
-	configDir   string
+	configDirs  []string
 	useRSA      bool
 	storage     certStorage
 	certs       []*certInfo
@@ -89,13 +88,16 @@ type Manager struct {
 // NewManager creates a new Manager with the given configuration.
 func NewManager(config *Config, certGen CertGenerator) (*Manager, error) {
 	// Validate the configuration.
-	if config.Dir == "" {
-		return nil, errors.New("configuration parameter 'cert_dir' is unset")
+	if len(config.Dirs) == 0 {
+		return nil, errors.New("configuration parameter 'config_dirs' is unset")
+	}
+	if config.Output.Path == "" {
+		return nil, errors.New("'output.path' is unset")
 	}
 
 	m := &Manager{
 		useRSA:      config.UseRSA,
-		configDir:   filepath.Join(config.Dir, "config"),
+		configDirs:  config.Dirs,
 		doneCh:      make(chan bool),
 		configCh:    make(chan []*certConfig, 1),
 		certGen:     certGen,
@@ -105,11 +107,11 @@ func NewManager(config *Config, certGen CertGenerator) (*Manager, error) {
 		m.renewalDays = 15
 	}
 
-	ds := &dirStorage{root: filepath.Join(config.Dir, "certs")}
-	if config.ReplDS == nil {
+	ds := &dirStorage{root: config.Output.Path}
+	if config.Output.ReplDS == nil {
 		m.storage = ds
 	} else {
-		r, err := replds.NewPublicClient(config.ReplDS)
+		r, err := replds.NewPublicClient(config.Output.ReplDS)
 		if err != nil {
 			return nil, err
 		}
@@ -126,7 +128,7 @@ func NewManager(config *Config, certGen CertGenerator) (*Manager, error) {
 // cause background processing to stop, interrupting all running
 // updates.
 func (m *Manager) Start(ctx context.Context) error {
-	domains, err := readCertConfigsFromDir(m.configDir)
+	domains, err := readCertConfigsFromDirs(m.configDirs)
 	if err != nil {
 		return err
 	}
@@ -145,7 +147,7 @@ func (m *Manager) Wait() {
 
 // Reload configuration.
 func (m *Manager) Reload() {
-	domains, err := readCertConfigsFromDir(m.configDir)
+	domains, err := readCertConfigsFromDirs(m.configDirs)
 	if err != nil {
 		log.Printf("error reading config: %v", err)
 		return
diff --git a/manager_test.go b/manager_test.go
index ced4b0cbab0079d05bac84e667294f6014b83465..895164e773e250be8efda21d4afb2a14ccb68560 100644
--- a/manager_test.go
+++ b/manager_test.go
@@ -26,6 +26,16 @@ func (s *slowACME) GetCertificate(ctx context.Context, _ crypto.Signer, _ *certC
 	}
 }
 
+func newTestConfig(dir string) *Config {
+	c := Config{
+		Dirs:           []string{filepath.Join(dir, "config")},
+		AccountKeyPath: filepath.Join(dir, "account.key"),
+		Email:          "test@example.com",
+	}
+	c.Output.Path = filepath.Join(dir, "certs")
+	return &c
+}
+
 // Create a new test function.
 // The first function returned is a cleanup callback.
 // The second function returned is the cancel callback.
@@ -42,10 +52,7 @@ func newTestManager(t testing.TB, g CertGenerator) (func(), context.CancelFunc,
 		0644,
 	)
 
-	m, err := NewManager(&Config{
-		Dir:   dir,
-		Email: "test@example.com",
-	}, g)
+	m, err := NewManager(newTestConfig(dir), g)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -114,11 +121,11 @@ func TestManager_NewCert(t *testing.T) {
 
 	// Verify that the credentials have successfully been written
 	// to storage.
-	p := filepath.Join(m.configDir, "../certs/example.com/cert.pem")
+	p := filepath.Join(m.configDirs[0], "../certs/example.com/cert.pem")
 	if _, err := os.Stat(p); err != nil {
 		t.Fatalf("file not created: %v", err)
 	}
-	p = filepath.Join(m.configDir, "../certs/example.com/private_key.pem")
+	p = filepath.Join(m.configDirs[0], "../certs/example.com/private_key.pem")
 	if _, err := os.Stat(p); err != nil {
 		t.Fatalf("file not created: %v", err)
 	}