diff --git a/scripts/sql/manage_storage.php b/scripts/sql/manage_storage.php
--- a/scripts/sql/manage_storage.php
+++ b/scripts/sql/manage_storage.php
@@ -148,12 +148,14 @@
   $default_user = $ref->getUser();
   $default_host = $ref->getHost();
   $default_port = $ref->getPort();
+  $default_use_tls = $ref->getUseTls();
 
   $test_api = id(new PhabricatorStorageManagementAPI())
     ->setUser($default_user)
     ->setHost($default_host)
     ->setPort($default_port)
     ->setPassword($ref->getPass())
+    ->setUseTls($default_use_tls)
     ->setNamespace($args->getArg('namespace'));
 
   try {
@@ -170,7 +172,8 @@
         'storage. Run these commands to set up credentials:'),
       "  $ ./bin/config set mysql.host __host__\n".
       "  $ ./bin/config set mysql.user __username__\n".
-      "  $ ./bin/config set mysql.pass __password__",
+      "  $ ./bin/config set mysql.pass __password__\n".
+      "  $ ./bin/config set mysql.use-tls __use-tls__",
       pht(
         'These standard credentials are separate from any administrative '.
         'credentials provided to this command with __%s__ or '.
@@ -202,6 +205,7 @@
     ->setHost($default_host)
     ->setPort($default_port)
     ->setPassword($password)
+    ->setUseTls($default_use_tls)
     ->setNamespace($args->getArg('namespace'))
     ->setDisableUTF8MB4($args->getArg('disable-utf8mb4'));
   PhabricatorEnv::overrideConfig('mysql.user', $api->getUser());
diff --git a/src/applications/config/option/PhabricatorMySQLConfigOptions.php b/src/applications/config/option/PhabricatorMySQLConfigOptions.php
--- a/src/applications/config/option/PhabricatorMySQLConfigOptions.php
+++ b/src/applications/config/option/PhabricatorMySQLConfigOptions.php
@@ -47,10 +47,14 @@
             "this namespace if you want. Normally, you should not do this ".
             "unless you are developing extensions and using namespaces to ".
             "separate multiple sandbox datasets.")),
-        $this->newOption('mysql.port', 'string', null)
+      $this->newOption('mysql.port', 'string', null)
         ->setLocked(true)
         ->setDescription(
           pht('MySQL port to use when connecting to the database.')),
+      $this->newOption('mysql.use-tls', 'bool', false)
+        ->setLocked(true)
+        ->setDescription(
+          pht('Whether to require a TLS connection when connecting to MySQL.')),
     );
   }
 
diff --git a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php
--- a/src/applications/config/schema/PhabricatorConfigSchemaQuery.php
+++ b/src/applications/config/schema/PhabricatorConfigSchemaQuery.php
@@ -45,6 +45,7 @@
       ->setUser($ref->getUser())
       ->setHost($ref->getHost())
       ->setPort($ref->getPort())
+      ->setUseTls($ref->getUseTls())
       ->setNamespace(PhabricatorLiskDAO::getDefaultStorageNamespace())
       ->setPassword($ref->getPass());
   }
diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRef.php b/src/infrastructure/cluster/PhabricatorDatabaseRef.php
--- a/src/infrastructure/cluster/PhabricatorDatabaseRef.php
+++ b/src/infrastructure/cluster/PhabricatorDatabaseRef.php
@@ -22,6 +22,7 @@
   private $port;
   private $user;
   private $pass;
+  private $useTls;
   private $disabled;
   private $isMaster;
   private $isIndividual;
@@ -80,6 +81,15 @@
     return $this->pass;
   }
 
+  public function setUseTls($use_tls) {
+    $this->useTls = $use_tls;
+    return $this;
+  }
+
+  public function getUseTls() {
+    return $this->useTls;
+  }
+
   public function setIsMaster($is_master) {
     $this->isMaster = $is_master;
     return $this;
@@ -325,12 +335,15 @@
     $default_pass = phutil_string_cast($default_pass);
     $default_pass = new PhutilOpaqueEnvelope($default_pass);
 
+    $default_use_tls = PhabricatorEnv::getEnvConfig('mysql.use-tls');
+
     $config = PhabricatorEnv::getEnvConfig('cluster.databases');
 
     return id(new PhabricatorDatabaseRefParser())
       ->setDefaultPort($default_port)
       ->setDefaultUser($default_user)
       ->setDefaultPass($default_pass)
+      ->setDefaultUseTls($default_use_tls)
       ->newRefs($config);
   }
 
@@ -611,12 +624,14 @@
       PhabricatorEnv::getEnvConfig('mysql.pass'));
     $default_host = PhabricatorEnv::getEnvConfig('mysql.host');
     $default_port = PhabricatorEnv::getEnvConfig('mysql.port');
+    $default_use_tls = PhabricatorEnv::getEnvConfig('mysql.use-tls');
 
     return id(new self())
       ->setUser($default_user)
       ->setPass($default_pass)
       ->setHost($default_host)
       ->setPort($default_port)
+      ->setUseTls($default_use_tls)
       ->setIsIndividual(true)
       ->setIsMaster(true)
       ->setIsDefaultPartition(true)
@@ -707,6 +722,7 @@
       'pass' => $this->getPass(),
       'host' => $this->getHost(),
       'port' => $this->getPort(),
+      'use-tls' => $this->getUseTls(),
       'database' => null,
       'retries' => $default_retries,
       'timeout' => $default_timeout,
diff --git a/src/infrastructure/cluster/PhabricatorDatabaseRefParser.php b/src/infrastructure/cluster/PhabricatorDatabaseRefParser.php
--- a/src/infrastructure/cluster/PhabricatorDatabaseRefParser.php
+++ b/src/infrastructure/cluster/PhabricatorDatabaseRefParser.php
@@ -6,6 +6,7 @@
   private $defaultPort = 3306;
   private $defaultUser;
   private $defaultPass;
+  private $defaultUseTls;
 
   public function setDefaultPort($default_port) {
     $this->defaultPort = $default_port;
@@ -34,10 +35,20 @@
     return $this->defaultPass;
   }
 
+  public function setDefaultUseTls($default_use_tls) {
+    $this->defaultUseTls = $default_use_tls;
+    return $this;
+  }
+
+  public function getDefaultUseTls() {
+    return $this->defaultUseTls;
+  }
+
   public function newRefs(array $config) {
     $default_port = $this->getDefaultPort();
     $default_user = $this->getDefaultUser();
     $default_pass = $this->getDefaultPass();
+    $default_use_tls = $this->getDefaultUseTls();
 
     $refs = array();
 
@@ -46,6 +57,7 @@
       $host = $server['host'];
       $port = idx($server, 'port', $default_port);
       $user = idx($server, 'user', $default_user);
+      $use_tls = idx($server, 'use-tls', $default_use_tls);
       $disabled = idx($server, 'disabled', false);
 
       $pass = idx($server, 'pass');
@@ -65,6 +77,7 @@
         ->setPort($port)
         ->setUser($user)
         ->setPass($pass)
+        ->setUseTls($use_tls)
         ->setDisabled($disabled)
         ->setIsMaster($is_master)
         ->setUsePersistentConnections($use_persistent);
diff --git a/src/infrastructure/storage/connection/mysql/AphrontMySQLDatabaseConnection.php b/src/infrastructure/storage/connection/mysql/AphrontMySQLDatabaseConnection.php
--- a/src/infrastructure/storage/connection/mysql/AphrontMySQLDatabaseConnection.php
+++ b/src/infrastructure/storage/connection/mysql/AphrontMySQLDatabaseConnection.php
@@ -36,6 +36,15 @@
           'mysql_connect()'));
     }
 
+    $use_tls = $this->getConfiguration('use-tls');
+    if ($use_tls) {
+      // TLS requires AphrontMySQLiDatabaseConnection
+      throw new Exception(
+        pht(
+          'Using TLS database connections requires the PHP "%s" extension.',
+          'mysqli'));
+    }
+
     $user = $this->getConfiguration('user');
     $host = $this->getConfiguration('host');
     $port = $this->getConfiguration('port');
diff --git a/src/infrastructure/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php b/src/infrastructure/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php
--- a/src/infrastructure/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php
+++ b/src/infrastructure/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php
@@ -71,6 +71,35 @@
       $conn->options(MYSQLI_OPT_CONNECT_TIMEOUT, $timeout);
     }
 
+    $flags = 0;
+    $use_tls = $this->getConfiguration('use-tls');
+    if ($use_tls) {
+      // Below ssl_set() will do nothing if openssl is not enabled, leading to a
+      // successful insecure connection if the database server does not disable
+      // plaintext connections.
+      if (!function_exists('openssl_encrypt')) {
+        throw new Exception(
+          pht(
+            'Using TLS database connections requires the PHP "%s" extension.',
+            'openssl'));
+      }
+
+      $flags = MYSQLI_CLIENT_SSL;
+
+      // TODO: Support client cert validation?
+      $client_key = null;
+      $client_cert = null;
+      // TODO: Configure allowed ciphers?
+      $ciphers = null;
+
+      $conn->ssl_set(
+        $client_key,
+        $client_cert,
+        null, // ca_certificate, load from php.ini/openssl.cafile
+        null, // ca_path, load from php.ini/openssl.capath
+        $ciphers);
+    }
+
     if ($this->getPersistent()) {
       $host = 'p:'.$host;
     }
@@ -82,7 +111,9 @@
       $user,
       $pass,
       $database,
-      $port);
+      $port,
+      null, // socket
+      $flags);
 
     $call_error = $trap->getErrorsAsString();
     $trap->destroy();
diff --git a/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php b/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php
--- a/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php
+++ b/src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php
@@ -6,6 +6,7 @@
   private $host;
   private $user;
   private $port;
+  private $useTls;
   private $password;
   private $namespace;
   private $conns = array();
@@ -76,6 +77,15 @@
     return $this->port;
   }
 
+  public function setUseTls($use_tls) {
+    $this->useTls = $use_tls;
+    return $this;
+  }
+
+  public function getUseTls() {
+    return $this->useTls;
+  }
+
   public function setRef(PhabricatorDatabaseRef $ref) {
     $this->ref = $ref;
     return $this;
@@ -131,6 +141,7 @@
           'pass'      => $this->password,
           'host'      => $this->host,
           'port'      => $this->port,
+          'use-tls'   => $this->useTls,
           'database'  => $fragment
             ? $database
             : null,