diff --git a/docs/setup/delegation.md b/docs/setup/delegation.md
new file mode 100644
index 0000000000..ee9cbb3b1c
--- /dev/null
+++ b/docs/setup/delegation.md
@@ -0,0 +1,102 @@
+# Delegation of incoming federation traffic
+
+In the following documentation, we use the term `server_name` to refer to that setting
+in your homeserver configuration file. It appears at the ends of user ids, and tells
+other homeservers where they can find your server.
+
+By default, other homeservers will expect to be able to reach yours via
+your `server_name`, on port 8448. For example, if you set your `server_name`
+to `example.com` (so that your user names look like `@user:example.com`),
+other servers will try to connect to yours at `https://example.com:8448/`.
+
+Delegation is a Matrix feature allowing a homeserver admin to retain a
+`server_name` of `example.com` so that user IDs, room aliases, etc continue
+to look like `*:example.com`, whilst having federation traffic routed
+to a different server and/or port (e.g. `synapse.example.com:443`).
+
+## .well-known delegation
+
+To use this method, you need to be able to configure the server at
+`https://<server_name>` to serve a file at
+`https://<server_name>/.well-known/matrix/server`. There are two ways to do this, shown below.
+
+Note that the `.well-known` file is hosted on the default port for `https` (port 443).
+
+### External server
+
+For maximum flexibility, you need to configure an external server such as nginx, Apache
+or HAProxy to serve the `https://<server_name>/.well-known/matrix/server` file. Setting
+up such a server is out of the scope of this documentation, but note that it is often
+possible to configure your [reverse proxy](reverse_proxy.md) for this.
+
+The URL `https://<server_name>/.well-known/matrix/server` should be configured
+return a JSON structure containing the key `m.server` like this:
+
+```json
+{
+ "m.server": "<synapse.server.name>[:<yourport>]"
+}
+```
+
+In our example (where we want federation traffic to be routed to
+`https://synapse.example.com`, on port 443), this would mean that
+`https://example.com/.well-known/matrix/server` should return:
+
+```json
+{
+ "m.server": "synapse.example.com:443"
+}
+```
+
+Note, specifying a port is optional. If no port is specified, then it defaults
+to 8448.
+
+### Serving a `.well-known/matrix/server` file with Synapse
+
+If you are able to set up your domain so that `https://<server_name>` is routed to
+Synapse (i.e., the only change needed is to direct federation traffic to port 443
+instead of port 8448), then it is possible to configure Synapse to serve a suitable
+`.well-known/matrix/server` file. To do so, add the following to your `homeserver.yaml`
+file:
+
+```yaml
+serve_server_wellknown: true
+```
+
+**Note**: this *only* works if `https://<server_name>` is routed to Synapse, so is
+generally not suitable if Synapse is hosted at a subdomain such as
+`https://synapse.example.com`.
+
+## SRV DNS record delegation
+
+It is also possible to do delegation using a SRV DNS record. However, that is generally
+not recommended, as it can be difficult to configure the TLS certificates correctly in
+this case, and it offers little advantage over `.well-known` delegation.
+
+However, if you really need it, you can find some documentation on what such a
+record should look like and how Synapse will use it in [the Matrix
+specification](https://matrix.org/docs/spec/server_server/latest#resolving-server-names).
+
+## Delegation FAQ
+
+### When do I need delegation?
+
+If your homeserver's APIs are accessible on the default federation port (8448)
+and the domain your `server_name` points to, you do not need any delegation.
+
+For instance, if you registered `example.com` and pointed its DNS A record at a
+fresh server, you could install Synapse on that host, giving it a `server_name`
+of `example.com`, and once a reverse proxy has been set up to proxy all requests
+sent to the port `8448` and serve TLS certificates for `example.com`, you
+wouldn't need any delegation set up.
+
+**However**, if your homeserver's APIs aren't accessible on port 8448 and on the
+domain `server_name` points to, you will need to let other servers know how to
+find it using delegation.
+
+### Should I use a reverse proxy for federation traffic?
+
+Generally, using a reverse proxy for both the federation and client traffic is a good
+idea, since it saves handling TLS traffic in Synapse. See
+[the reverse proxy documentation](reverse_proxy.md) for information on setting up a
+reverse proxy.
diff --git a/docs/setup/postgres.md b/docs/setup/postgres.md
new file mode 100644
index 0000000000..083b0aaff0
--- /dev/null
+++ b/docs/setup/postgres.md
@@ -0,0 +1,255 @@
+# Using Postgres
+
+Synapse supports PostgreSQL versions 9.6 or later.
+
+## Install postgres client libraries
+
+Synapse will require the python postgres client library in order to
+connect to a postgres database.
+
+- If you are using the [matrix.org debian/ubuntu
+ packages](setup/installation.md#matrixorg-packages), the necessary python
+ library will already be installed, but you will need to ensure the
+ low-level postgres library is installed, which you can do with
+ `apt install libpq5`.
+- For other pre-built packages, please consult the documentation from
+ the relevant package.
+- If you installed synapse [in a
+ virtualenv](setup/installation.md#installing-from-source), you can install
+ the library with:
+
+ ~/synapse/env/bin/pip install "matrix-synapse[postgres]"
+
+ (substituting the path to your virtualenv for `~/synapse/env`, if
+ you used a different path). You will require the postgres
+ development files. These are in the `libpq-dev` package on
+ Debian-derived distributions.
+
+## Set up database
+
+Assuming your PostgreSQL database user is called `postgres`, first authenticate as the database user with:
+
+```sh
+su - postgres
+# Or, if your system uses sudo to get administrative rights
+sudo -u postgres bash
+```
+
+Then, create a postgres user and a database with:
+
+```sh
+# this will prompt for a password for the new user
+createuser --pwprompt synapse_user
+
+createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse_user synapse
+```
+
+The above will create a user called `synapse_user`, and a database called
+`synapse`.
+
+Note that the PostgreSQL database *must* have the correct encoding set
+(as shown above), otherwise it will not be able to store UTF8 strings.
+
+You may need to enable password authentication so `synapse_user` can
+connect to the database. See
+<https://www.postgresql.org/docs/current/auth-pg-hba-conf.html>.
+
+## Synapse config
+
+When you are ready to start using PostgreSQL, edit the `database`
+section in your config file to match the following lines:
+
+```yaml
+database:
+ name: psycopg2
+ args:
+ user: <user>
+ password: <pass>
+ database: <db>
+ host: <host>
+ cp_min: 5
+ cp_max: 10
+```
+
+All key, values in `args` are passed to the `psycopg2.connect(..)`
+function, except keys beginning with `cp_`, which are consumed by the
+twisted adbapi connection pool. See the [libpq
+documentation](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS)
+for a list of options which can be passed.
+
+You should consider tuning the `args.keepalives_*` options if there is any danger of
+the connection between your homeserver and database dropping, otherwise Synapse
+may block for an extended period while it waits for a response from the
+database server. Example values might be:
+
+```yaml
+database:
+ args:
+ # ... as above
+
+ # seconds of inactivity after which TCP should send a keepalive message to the server
+ keepalives_idle: 10
+
+ # the number of seconds after which a TCP keepalive message that is not
+ # acknowledged by the server should be retransmitted
+ keepalives_interval: 10
+
+ # the number of TCP keepalives that can be lost before the client's connection
+ # to the server is considered dead
+ keepalives_count: 3
+```
+
+## Tuning Postgres
+
+The default settings should be fine for most deployments. For larger
+scale deployments tuning some of the settings is recommended, details of
+which can be found at
+<https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server>.
+
+In particular, we've found tuning the following values helpful for
+performance:
+
+- `shared_buffers`
+- `effective_cache_size`
+- `work_mem`
+- `maintenance_work_mem`
+- `autovacuum_work_mem`
+
+Note that the appropriate values for those fields depend on the amount
+of free memory the database host has available.
+
+
+## Porting from SQLite
+
+### Overview
+
+The script `synapse_port_db` allows porting an existing synapse server
+backed by SQLite to using PostgreSQL. This is done in as a two phase
+process:
+
+1. Copy the existing SQLite database to a separate location and run
+ the port script against that offline database.
+2. Shut down the server. Rerun the port script to port any data that
+ has come in since taking the first snapshot. Restart server against
+ the PostgreSQL database.
+
+The port script is designed to be run repeatedly against newer snapshots
+of the SQLite database file. This makes it safe to repeat step 1 if
+there was a delay between taking the previous snapshot and being ready
+to do step 2.
+
+It is safe to at any time kill the port script and restart it.
+
+Note that the database may take up significantly more (25% - 100% more)
+space on disk after porting to Postgres.
+
+### Using the port script
+
+Firstly, shut down the currently running synapse server and copy its
+database file (typically `homeserver.db`) to another location. Once the
+copy is complete, restart synapse. For instance:
+
+```sh
+./synctl stop
+cp homeserver.db homeserver.db.snapshot
+./synctl start
+```
+
+Copy the old config file into a new config file:
+
+```sh
+cp homeserver.yaml homeserver-postgres.yaml
+```
+
+Edit the database section as described in the section *Synapse config*
+above and with the SQLite snapshot located at `homeserver.db.snapshot`
+simply run:
+
+```sh
+synapse_port_db --sqlite-database homeserver.db.snapshot \
+ --postgres-config homeserver-postgres.yaml
+```
+
+The flag `--curses` displays a coloured curses progress UI.
+
+If the script took a long time to complete, or time has otherwise passed
+since the original snapshot was taken, repeat the previous steps with a
+newer snapshot.
+
+To complete the conversion shut down the synapse server and run the port
+script one last time, e.g. if the SQLite database is at `homeserver.db`
+run:
+
+```sh
+synapse_port_db --sqlite-database homeserver.db \
+ --postgres-config homeserver-postgres.yaml
+```
+
+Once that has completed, change the synapse config to point at the
+PostgreSQL database configuration file `homeserver-postgres.yaml`:
+
+```sh
+./synctl stop
+mv homeserver.yaml homeserver-old-sqlite.yaml
+mv homeserver-postgres.yaml homeserver.yaml
+./synctl start
+```
+
+Synapse should now be running against PostgreSQL.
+
+
+## Troubleshooting
+
+### Alternative auth methods
+
+If you get an error along the lines of `FATAL: Ident authentication failed for
+user "synapse_user"`, you may need to use an authentication method other than
+`ident`:
+
+* If the `synapse_user` user has a password, add the password to the `database:`
+ section of `homeserver.yaml`. Then add the following to `pg_hba.conf`:
+
+ ```
+ host synapse synapse_user ::1/128 md5 # or `scram-sha-256` instead of `md5` if you use that
+ ```
+
+* If the `synapse_user` user does not have a password, then a password doesn't
+ have to be added to `homeserver.yaml`. But the following does need to be added
+ to `pg_hba.conf`:
+
+ ```
+ host synapse synapse_user ::1/128 trust
+ ```
+
+Note that line order matters in `pg_hba.conf`, so make sure that if you do add a
+new line, it is inserted before:
+
+```
+host all all ::1/128 ident
+```
+
+### Fixing incorrect `COLLATE` or `CTYPE`
+
+Synapse will refuse to set up a new database if it has the wrong values of
+`COLLATE` and `CTYPE` set, and will log warnings on existing databases. Using
+different locales can cause issues if the locale library is updated from
+underneath the database, or if a different version of the locale is used on any
+replicas.
+
+The safest way to fix the issue is to dump the database and recreate it with
+the correct locale parameter (as shown above). It is also possible to change the
+parameters on a live database and run a `REINDEX` on the entire database,
+however extreme care must be taken to avoid database corruption.
+
+Note that the above may fail with an error about duplicate rows if corruption
+has already occurred, and such duplicate rows will need to be manually removed.
+
+### Fixing inconsistent sequences error
+
+Synapse uses Postgres sequences to generate IDs for various tables. A sequence
+and associated table can get out of sync if, for example, Synapse has been
+downgraded and then upgraded again.
+
+To fix the issue shut down Synapse (including any and all workers) and run the
+SQL command included in the error message. Once done Synapse should start
+successfully.
diff --git a/docs/setup/reverse_proxy.md b/docs/setup/reverse_proxy.md
new file mode 100644
index 0000000000..f3b3aea732
--- /dev/null
+++ b/docs/setup/reverse_proxy.md
@@ -0,0 +1,267 @@
+# Using a reverse proxy with Synapse
+
+It is recommended to put a reverse proxy such as
+[nginx](https://nginx.org/en/docs/http/ngx_http_proxy_module.html),
+[Apache](https://httpd.apache.org/docs/current/mod/mod_proxy_http.html),
+[Caddy](https://caddyserver.com/docs/quick-starts/reverse-proxy),
+[HAProxy](https://www.haproxy.org/) or
+[relayd](https://man.openbsd.org/relayd.8) in front of Synapse. One advantage
+of doing so is that it means that you can expose the default https port
+(443) to Matrix clients without needing to run Synapse with root
+privileges.
+
+You should configure your reverse proxy to forward requests to `/_matrix` or
+`/_synapse/client` to Synapse, and have it set the `X-Forwarded-For` and
+`X-Forwarded-Proto` request headers.
+
+You should remember that Matrix clients and other Matrix servers do not
+necessarily need to connect to your server via the same server name or
+port. Indeed, clients will use port 443 by default, whereas servers default to
+port 8448. Where these are different, we refer to the 'client port' and the
+'federation port'. See [the Matrix
+specification](https://matrix.org/docs/spec/server_server/latest#resolving-server-names)
+for more details of the algorithm used for federation connections, and
+[Delegation](delegate.md) for instructions on setting up delegation.
+
+**NOTE**: Your reverse proxy must not `canonicalise` or `normalise`
+the requested URI in any way (for example, by decoding `%xx` escapes).
+Beware that Apache *will* canonicalise URIs unless you specify
+`nocanon`.
+
+Let's assume that we expect clients to connect to our server at
+`https://matrix.example.com`, and other servers to connect at
+`https://example.com:8448`. The following sections detail the configuration of
+the reverse proxy and the homeserver.
+
+
+## Homeserver Configuration
+
+The HTTP configuration will need to be updated for Synapse to correctly record
+client IP addresses and generate redirect URLs while behind a reverse proxy.
+
+In `homeserver.yaml` set `x_forwarded: true` in the port 8008 section and
+consider setting `bind_addresses: ['127.0.0.1']` so that the server only
+listens to traffic on localhost. (Do not change `bind_addresses` to `127.0.0.1`
+when using a containerized Synapse, as that will prevent it from responding
+to proxied traffic.)
+
+
+## Reverse-proxy configuration examples
+
+**NOTE**: You only need one of these.
+
+### nginx
+
+```nginx
+server {
+ listen 443 ssl http2;
+ listen [::]:443 ssl http2;
+
+ # For the federation port
+ listen 8448 ssl http2 default_server;
+ listen [::]:8448 ssl http2 default_server;
+
+ server_name matrix.example.com;
+
+ location ~* ^(\/_matrix|\/_synapse\/client) {
+ # note: do not add a path (even a single /) after the port in `proxy_pass`,
+ # otherwise nginx will canonicalise the URI and cause signature verification
+ # errors.
+ proxy_pass http://localhost:8008;
+ proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_set_header Host $host;
+
+ # Nginx by default only allows file uploads up to 1M in size
+ # Increase client_max_body_size to match max_upload_size defined in homeserver.yaml
+ client_max_body_size 50M;
+ }
+}
+```
+
+### Caddy v1
+
+```
+matrix.example.com {
+ proxy /_matrix http://localhost:8008 {
+ transparent
+ }
+
+ proxy /_synapse/client http://localhost:8008 {
+ transparent
+ }
+}
+
+example.com:8448 {
+ proxy / http://localhost:8008 {
+ transparent
+ }
+}
+```
+
+### Caddy v2
+
+```
+matrix.example.com {
+ reverse_proxy /_matrix/* http://localhost:8008
+ reverse_proxy /_synapse/client/* http://localhost:8008
+}
+
+example.com:8448 {
+ reverse_proxy http://localhost:8008
+}
+```
+[Delegation](delegate.md) example:
+```
+(matrix-well-known-header) {
+ # Headers
+ header Access-Control-Allow-Origin "*"
+ header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
+ header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept, Authorization"
+ header Content-Type "application/json"
+}
+
+example.com {
+ handle /.well-known/matrix/server {
+ import matrix-well-known-header
+ respond `{"m.server":"matrix.example.com:443"}`
+ }
+
+ handle /.well-known/matrix/client {
+ import matrix-well-known-header
+ respond `{"m.homeserver":{"base_url":"https://matrix.example.com"},"m.identity_server":{"base_url":"https://identity.example.com"}}`
+ }
+}
+
+matrix.example.com {
+ reverse_proxy /_matrix/* http://localhost:8008
+ reverse_proxy /_synapse/client/* http://localhost:8008
+}
+```
+
+### Apache
+
+```apache
+<VirtualHost *:443>
+ SSLEngine on
+ ServerName matrix.example.com
+
+ RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
+ AllowEncodedSlashes NoDecode
+ ProxyPreserveHost on
+ ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
+ ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
+ ProxyPass /_synapse/client http://127.0.0.1:8008/_synapse/client nocanon
+ ProxyPassReverse /_synapse/client http://127.0.0.1:8008/_synapse/client
+</VirtualHost>
+
+<VirtualHost *:8448>
+ SSLEngine on
+ ServerName example.com
+
+ RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
+ AllowEncodedSlashes NoDecode
+ ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
+ ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
+</VirtualHost>
+```
+
+**NOTE**: ensure the `nocanon` options are included.
+
+**NOTE 2**: It appears that Synapse is currently incompatible with the ModSecurity module for Apache (`mod_security2`). If you need it enabled for other services on your web server, you can disable it for Synapse's two VirtualHosts by including the following lines before each of the two `</VirtualHost>` above:
+
+```apache
+<IfModule security2_module>
+ SecRuleEngine off
+</IfModule>
+```
+
+**NOTE 3**: Missing `ProxyPreserveHost on` can lead to a redirect loop.
+
+### HAProxy
+
+```
+frontend https
+ bind :::443 v4v6 ssl crt /etc/ssl/haproxy/ strict-sni alpn h2,http/1.1
+ http-request set-header X-Forwarded-Proto https if { ssl_fc }
+ http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
+ http-request set-header X-Forwarded-For %[src]
+
+ # Matrix client traffic
+ acl matrix-host hdr(host) -i matrix.example.com matrix.example.com:443
+ acl matrix-path path_beg /_matrix
+ acl matrix-path path_beg /_synapse/client
+
+ use_backend matrix if matrix-host matrix-path
+
+frontend matrix-federation
+ bind :::8448 v4v6 ssl crt /etc/ssl/haproxy/synapse.pem alpn h2,http/1.1
+ http-request set-header X-Forwarded-Proto https if { ssl_fc }
+ http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
+ http-request set-header X-Forwarded-For %[src]
+
+ default_backend matrix
+
+backend matrix
+ server matrix 127.0.0.1:8008
+```
+
+### Relayd
+
+```
+table <webserver> { 127.0.0.1 }
+table <matrixserver> { 127.0.0.1 }
+
+http protocol "https" {
+ tls { no tlsv1.0, ciphers "HIGH" }
+ tls keypair "example.com"
+ match header set "X-Forwarded-For" value "$REMOTE_ADDR"
+ match header set "X-Forwarded-Proto" value "https"
+
+ # set CORS header for .well-known/matrix/server, .well-known/matrix/client
+ # httpd does not support setting headers, so do it here
+ match request path "/.well-known/matrix/*" tag "matrix-cors"
+ match response tagged "matrix-cors" header set "Access-Control-Allow-Origin" value "*"
+
+ pass quick path "/_matrix/*" forward to <matrixserver>
+ pass quick path "/_synapse/client/*" forward to <matrixserver>
+
+ # pass on non-matrix traffic to webserver
+ pass forward to <webserver>
+}
+
+relay "https_traffic" {
+ listen on egress port 443 tls
+ protocol "https"
+ forward to <matrixserver> port 8008 check tcp
+ forward to <webserver> port 8080 check tcp
+}
+
+http protocol "matrix" {
+ tls { no tlsv1.0, ciphers "HIGH" }
+ tls keypair "example.com"
+ block
+ pass quick path "/_matrix/*" forward to <matrixserver>
+ pass quick path "/_synapse/client/*" forward to <matrixserver>
+}
+
+relay "matrix_federation" {
+ listen on egress port 8448 tls
+ protocol "matrix"
+ forward to <matrixserver> port 8008 check tcp
+}
+```
+
+
+## Health check endpoint
+
+Synapse exposes a health check endpoint for use by reverse proxies.
+Each configured HTTP listener has a `/health` endpoint which always returns
+200 OK (and doesn't get logged).
+
+## Synapse administration endpoints
+
+Endpoints for administering your Synapse instance are placed under
+`/_synapse/admin`. These require authentication through an access token of an
+admin user. However as access to these endpoints grants the caller a lot of power,
+we do not recommend exposing them to the public internet without good reason.
diff --git a/docs/setup/turn-howto.md b/docs/setup/turn-howto.md
new file mode 100644
index 0000000000..e6812de69e
--- /dev/null
+++ b/docs/setup/turn-howto.md
@@ -0,0 +1,296 @@
+# Overview
+
+This document explains how to enable VoIP relaying on your homeserver with
+TURN.
+
+The synapse Matrix homeserver supports integration with TURN server via the
+[TURN server REST API](<https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00>). This
+allows the homeserver to generate credentials that are valid for use on the
+TURN server through the use of a secret shared between the homeserver and the
+TURN server.
+
+The following sections describe how to install [coturn](<https://github.com/coturn/coturn>) (which implements the TURN REST API) and integrate it with synapse.
+
+## Requirements
+
+For TURN relaying with `coturn` to work, it must be hosted on a server/endpoint with a public IP.
+
+Hosting TURN behind a NAT (even with appropriate port forwarding) is known to cause issues
+and to often not work.
+
+## `coturn` setup
+
+### Initial installation
+
+The TURN daemon `coturn` is available from a variety of sources such as native package managers, or installation from source.
+
+#### Debian installation
+
+Just install the debian package:
+
+```sh
+apt install coturn
+```
+
+This will install and start a systemd service called `coturn`.
+
+#### Source installation
+
+1. Download the [latest release](https://github.com/coturn/coturn/releases/latest) from github. Unpack it and `cd` into the directory.
+
+1. Configure it:
+
+ ```sh
+ ./configure
+ ```
+
+ You may need to install `libevent2`: if so, you should do so in
+ the way recommended by your operating system. You can ignore
+ warnings about lack of database support: a database is unnecessary
+ for this purpose.
+
+1. Build and install it:
+
+ ```sh
+ make
+ make install
+ ```
+
+### Configuration
+
+1. Create or edit the config file in `/etc/turnserver.conf`. The relevant
+ lines, with example values, are:
+
+ ```
+ use-auth-secret
+ static-auth-secret=[your secret key here]
+ realm=turn.myserver.org
+ ```
+
+ See `turnserver.conf` for explanations of the options. One way to generate
+ the `static-auth-secret` is with `pwgen`:
+
+ ```sh
+ pwgen -s 64 1
+ ```
+
+ A `realm` must be specified, but its value is somewhat arbitrary. (It is
+ sent to clients as part of the authentication flow.) It is conventional to
+ set it to be your server name.
+
+1. You will most likely want to configure coturn to write logs somewhere. The
+ easiest way is normally to send them to the syslog:
+
+ ```sh
+ syslog
+ ```
+
+ (in which case, the logs will be available via `journalctl -u coturn` on a
+ systemd system). Alternatively, coturn can be configured to write to a
+ logfile - check the example config file supplied with coturn.
+
+1. Consider your security settings. TURN lets users request a relay which will
+ connect to arbitrary IP addresses and ports. The following configuration is
+ suggested as a minimum starting point:
+
+ ```
+ # VoIP traffic is all UDP. There is no reason to let users connect to arbitrary TCP endpoints via the relay.
+ no-tcp-relay
+
+ # don't let the relay ever try to connect to private IP address ranges within your network (if any)
+ # given the turn server is likely behind your firewall, remember to include any privileged public IPs too.
+ denied-peer-ip=10.0.0.0-10.255.255.255
+ denied-peer-ip=192.168.0.0-192.168.255.255
+ denied-peer-ip=172.16.0.0-172.31.255.255
+
+ # special case the turn server itself so that client->TURN->TURN->client flows work
+ allowed-peer-ip=10.0.0.1
+
+ # consider whether you want to limit the quota of relayed streams per user (or total) to avoid risk of DoS.
+ user-quota=12 # 4 streams per video call, so 12 streams = 3 simultaneous relayed calls per user.
+ total-quota=1200
+ ```
+
+1. Also consider supporting TLS/DTLS. To do this, add the following settings
+ to `turnserver.conf`:
+
+ ```
+ # TLS certificates, including intermediate certs.
+ # For Let's Encrypt certificates, use `fullchain.pem` here.
+ cert=/path/to/fullchain.pem
+
+ # TLS private key file
+ pkey=/path/to/privkey.pem
+ ```
+
+ In this case, replace the `turn:` schemes in the `turn_uri` settings below
+ with `turns:`.
+
+ We recommend that you only try to set up TLS/DTLS once you have set up a
+ basic installation and got it working.
+
+1. Ensure your firewall allows traffic into the TURN server on the ports
+ you've configured it to listen on (By default: 3478 and 5349 for TURN
+ traffic (remember to allow both TCP and UDP traffic), and ports 49152-65535
+ for the UDP relay.)
+
+1. We do not recommend running a TURN server behind NAT, and are not aware of
+ anyone doing so successfully.
+
+ If you want to try it anyway, you will at least need to tell coturn its
+ external IP address:
+
+ ```
+ external-ip=192.88.99.1
+ ```
+
+ ... and your NAT gateway must forward all of the relayed ports directly
+ (eg, port 56789 on the external IP must be always be forwarded to port
+ 56789 on the internal IP).
+
+ If you get this working, let us know!
+
+1. (Re)start the turn server:
+
+ * If you used the Debian package (or have set up a systemd unit yourself):
+ ```sh
+ systemctl restart coturn
+ ```
+
+ * If you installed from source:
+
+ ```sh
+ bin/turnserver -o
+ ```
+
+## Synapse setup
+
+Your homeserver configuration file needs the following extra keys:
+
+1. "`turn_uris`": This needs to be a yaml list of public-facing URIs
+ for your TURN server to be given out to your clients. Add separate
+ entries for each transport your TURN server supports.
+2. "`turn_shared_secret`": This is the secret shared between your
+ homeserver and your TURN server, so you should set it to the same
+ string you used in turnserver.conf.
+3. "`turn_user_lifetime`": This is the amount of time credentials
+ generated by your homeserver are valid for (in milliseconds).
+ Shorter times offer less potential for abuse at the expense of
+ increased traffic between web clients and your homeserver to
+ refresh credentials. The TURN REST API specification recommends
+ one day (86400000).
+4. "`turn_allow_guests`": Whether to allow guest users to use the
+ TURN server. This is enabled by default, as otherwise VoIP will
+ not work reliably for guests. However, it does introduce a
+ security risk as it lets guests connect to arbitrary endpoints
+ without having gone through a CAPTCHA or similar to register a
+ real account.
+
+As an example, here is the relevant section of the config file for `matrix.org`. The
+`turn_uris` are appropriate for TURN servers listening on the default ports, with no TLS.
+
+ turn_uris: [ "turn:turn.matrix.org?transport=udp", "turn:turn.matrix.org?transport=tcp" ]
+ turn_shared_secret: "n0t4ctuAllymatr1Xd0TorgSshar3d5ecret4obvIousreAsons"
+ turn_user_lifetime: 86400000
+ turn_allow_guests: True
+
+After updating the homeserver configuration, you must restart synapse:
+
+ * If you use synctl:
+ ```sh
+ cd /where/you/run/synapse
+ ./synctl restart
+ ```
+ * If you use systemd:
+ ```sh
+ systemctl restart matrix-synapse.service
+ ```
+... and then reload any clients (or wait an hour for them to refresh their
+settings).
+
+## Troubleshooting
+
+The normal symptoms of a misconfigured TURN server are that calls between
+devices on different networks ring, but get stuck at "call
+connecting". Unfortunately, troubleshooting this can be tricky.
+
+Here are a few things to try:
+
+ * Check that your TURN server is not behind NAT. As above, we're not aware of
+ anyone who has successfully set this up.
+
+ * Check that you have opened your firewall to allow TCP and UDP traffic to the
+ TURN ports (normally 3478 and 5349).
+
+ * Check that you have opened your firewall to allow UDP traffic to the UDP
+ relay ports (49152-65535 by default).
+
+ * Some WebRTC implementations (notably, that of Google Chrome) appear to get
+ confused by TURN servers which are reachable over IPv6 (this appears to be
+ an unexpected side-effect of its handling of multiple IP addresses as
+ defined by
+ [`draft-ietf-rtcweb-ip-handling`](https://tools.ietf.org/html/draft-ietf-rtcweb-ip-handling-12)).
+
+ Try removing any AAAA records for your TURN server, so that it is only
+ reachable over IPv4.
+
+ * Enable more verbose logging in coturn via the `verbose` setting:
+
+ ```
+ verbose
+ ```
+
+ ... and then see if there are any clues in its logs.
+
+ * If you are using a browser-based client under Chrome, check
+ `chrome://webrtc-internals/` for insights into the internals of the
+ negotiation. On Firefox, check the "Connection Log" on `about:webrtc`.
+
+ (Understanding the output is beyond the scope of this document!)
+
+ * You can test your Matrix homeserver TURN setup with https://test.voip.librepush.net/.
+ Note that this test is not fully reliable yet, so don't be discouraged if
+ the test fails.
+ [Here](https://github.com/matrix-org/voip-tester) is the github repo of the
+ source of the tester, where you can file bug reports.
+
+ * There is a WebRTC test tool at
+ https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/. To
+ use it, you will need a username/password for your TURN server. You can
+ either:
+
+ * look for the `GET /_matrix/client/r0/voip/turnServer` request made by a
+ matrix client to your homeserver in your browser's network inspector. In
+ the response you should see `username` and `password`. Or:
+
+ * Use the following shell commands:
+
+ ```sh
+ secret=staticAuthSecretHere
+
+ u=$((`date +%s` + 3600)):test
+ p=$(echo -n $u | openssl dgst -hmac $secret -sha1 -binary | base64)
+ echo -e "username: $u\npassword: $p"
+ ```
+
+ Or:
+
+ * Temporarily configure coturn to accept a static username/password. To do
+ this, comment out `use-auth-secret` and `static-auth-secret` and add the
+ following:
+
+ ```
+ lt-cred-mech
+ user=username:password
+ ```
+
+ **Note**: these settings will not take effect unless `use-auth-secret`
+ and `static-auth-secret` are disabled.
+
+ Restart coturn after changing the configuration file.
+
+ Remember to restore the original settings to go back to testing with
+ Matrix clients!
+
+ If the TURN server is working correctly, you should see at least one `relay`
+ entry in the results.
|