diff --git a/Dockerfile b/Dockerfile
index 3a3fcad28ffdc8348665133052acb98c8bbb0186..5cd7d54b12dffb7b5e4de19eace46593ad4f5336 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -73,6 +73,8 @@ COPY --from=0 /usr/lib/*-linux-gnu*/libcrypto.so* /usr/lib/
 
 # Swarm TCP; should be exposed to the public
 EXPOSE 4001
+# Swarm UDP; should be exposed to the public
+EXPOSE 4001/udp
 # Daemon API; must not be exposed publicly but to client services under you control
 EXPOSE 5001
 # Web Gateway; can be exposed publicly with a proxy, e.g. as https://ipfs.example.org
diff --git a/README.md b/README.md
index 8aae778ac6fde9cc20f80e0bb838d8997755cde3..00548dbb0e470ce8a6b5a39acc05aff4ecfa4ff9 100644
--- a/README.md
+++ b/README.md
@@ -344,7 +344,7 @@ IPFS files that will persist when you restart the container.
 
 Start a container running ipfs and expose ports 4001, 5001 and 8080:
 
-    docker run -d --name ipfs_host -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
+    docker run -d --name ipfs_host -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 4001:4001/udp -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
 
 Watch the ipfs log:
 
@@ -376,16 +376,16 @@ Stop the running container:
 
 When starting a container running ipfs for the first time with an empty data directory, it will call `ipfs init` to initialize configuration files and generate a new keypair. At this time, you can choose which profile to apply using the `IPFS_PROFILE` environment variable:
 
-    docker run -d --name ipfs_host -e IPFS_PROFILE=server -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
+    docker run -d --name ipfs_host -e IPFS_PROFILE=server -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 4001:4001/udp -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
 
 It is possible to initialize the container with a swarm key file (`/data/ipfs/swarm.key`) using the variables `IPFS_SWARM_KEY` and `IPFS_SWARM_KEY_FILE`. The `IPFS_SWARM_KEY` creates `swarm.key` with the contents of the variable itself, whilst `IPFS_SWARM_KEY_FILE` copies the key from a path stored in the variable. The `IPFS_SWARM_KEY_FILE` **overwrites** the key generated by `IPFS_SWARM_KEY`.
 
-    docker run -d --name ipfs_host -e IPFS_SWARM_KEY=<your swarm key> -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
+    docker run -d --name ipfs_host -e IPFS_SWARM_KEY=<your swarm key> -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 4001:4001/udp -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
 
 The swarm key initialization can also be done using docker secrets **(requires docker swarm or docker-compose)**:
 
     cat your_swarm.key | docker secret create swarm_key_secret -
-    docker run -d --name ipfs_host --secret swarm_key_secret -e IPFS_SWARM_KEY_FILE=/run/secrets/swarm_key_secret -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
+    docker run -d --name ipfs_host --secret swarm_key_secret -e IPFS_SWARM_KEY_FILE=/run/secrets/swarm_key_secret -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 4001:4001/udp -p 127.0.0.1:8080:8080 -p 127.0.0.1:5001:5001 ipfs/go-ipfs:latest
 
 ### Troubleshooting
 
diff --git a/core/core_test.go b/core/core_test.go
index 3a5f7c23682a4efd9dec1e4eeaa17a119d7d22d1..051b812c11ad51da53e5809ddb3fb38082bdc0c4 100644
--- a/core/core_test.go
+++ b/core/core_test.go
@@ -20,7 +20,7 @@ func TestInitialization(t *testing.T) {
 		{
 			Identity: id,
 			Addresses: config.Addresses{
-				Swarm: []string{"/ip4/0.0.0.0/tcp/4001"},
+				Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic"},
 				API:   []string{"/ip4/127.0.0.1/tcp/8000"},
 			},
 		},
@@ -28,7 +28,7 @@ func TestInitialization(t *testing.T) {
 		{
 			Identity: id,
 			Addresses: config.Addresses{
-				Swarm: []string{"/ip4/0.0.0.0/tcp/4001"},
+				Swarm: []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic"},
 				API:   []string{"/ip4/127.0.0.1/tcp/8000"},
 			},
 		},
diff --git a/core/node/builder.go b/core/node/builder.go
index 2dd1986ca3c7eda3b9969f2f39b7dd9fa6eb2de3..22ab500e15047417b9bc28d496b2f8741b0b3731 100644
--- a/core/node/builder.go
+++ b/core/node/builder.go
@@ -140,7 +140,7 @@ func defaultRepo(dstore repo.Datastore) (repo.Repo, error) {
 	}
 
 	c.Bootstrap = cfg.DefaultBootstrapAddresses
-	c.Addresses.Swarm = []string{"/ip4/0.0.0.0/tcp/4001"}
+	c.Addresses.Swarm = []string{"/ip4/0.0.0.0/tcp/4001", "/ip4/0.0.0.0/udp/4001/quic"}
 	c.Identity.PeerID = pid.Pretty()
 	c.Identity.PrivKey = base64.StdEncoding.EncodeToString(privkeyb)
 
diff --git a/core/node/groups.go b/core/node/groups.go
index f94d97634b10b9bbc64f3c4d3745f4aa07752542..4a769c34f972bc18eb46aeae0f3be4ae8703455b 100644
--- a/core/node/groups.go
+++ b/core/node/groups.go
@@ -130,7 +130,6 @@ func LibP2P(bcfg *BuildCfg, cfg *config.Config) fx.Option {
 		maybeProvide(libp2p.BandwidthCounter, !cfg.Swarm.DisableBandwidthMetrics),
 		maybeProvide(libp2p.NatPortMap, !cfg.Swarm.DisableNatPortMap),
 		maybeProvide(libp2p.AutoRelay, cfg.Swarm.EnableAutoRelay),
-		maybeProvide(libp2p.QUIC, cfg.Experimental.QUIC),
 		autonat,
 		connmgr,
 		ps,
diff --git a/core/node/libp2p/transport.go b/core/node/libp2p/transport.go
index 3994da5c5216750faadd42cd116507104396ef46..59e3c4d792768a0edbe733ff62c701c0840d8615 100644
--- a/core/node/libp2p/transport.go
+++ b/core/node/libp2p/transport.go
@@ -8,7 +8,7 @@ import (
 	tls "github.com/libp2p/go-libp2p-tls"
 )
 
-var DefaultTransports = simpleOpt(libp2p.DefaultTransports)
+var DefaultTransports = simpleOpt(libp2p.ChainOptions(libp2p.DefaultTransports, libp2p.Transport(libp2pquic.NewTransport)))
 var QUIC = simpleOpt(libp2p.Transport(libp2pquic.NewTransport))
 
 func Security(enabled bool) interface{} {
diff --git a/docs/config.md b/docs/config.md
index 2a64275bd26bbb3d0102d761f58d8e7aa530a693..f2211954cb89040a3bb1d563e0eb1e5e718c56aa 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -200,7 +200,9 @@ Default:
 ```json
 [
   "/ip4/0.0.0.0/tcp/4001",
-  "/ip6/::/tcp/4001"
+  "/ip6/::/tcp/4001",
+  "/ip6/0.0.0.0/udp/4001/quic",
+  "/ip6/::/udp/4001/quic"
 ]
 ```
 
diff --git a/docs/examples/go-ipfs-as-a-library/main.go b/docs/examples/go-ipfs-as-a-library/main.go
index cc390ecebf7db429ecb928b6b936873b78a35a69..f07398c307da45a79ce00b7bb795cd2916bcd8d1 100644
--- a/docs/examples/go-ipfs-as-a-library/main.go
+++ b/docs/examples/go-ipfs-as-a-library/main.go
@@ -300,12 +300,17 @@ func main() {
 
 		// IPFS Cluster Pinning nodes
 		"/ip4/138.201.67.219/tcp/4001/p2p/QmUd6zHcbkbcs7SMxwLs48qZVX3vpcM8errYS7xEczwRMA",
+		"/ip4/138.201.67.219/udp/4001/quic/p2p/QmUd6zHcbkbcs7SMxwLs48qZVX3vpcM8errYS7xEczwRMA",
 		"/ip4/138.201.67.220/tcp/4001/p2p/QmNSYxZAiJHeLdkBg38roksAR9So7Y5eojks1yjEcUtZ7i",
+		"/ip4/138.201.67.220/udp/4001/quic/p2p/QmNSYxZAiJHeLdkBg38roksAR9So7Y5eojks1yjEcUtZ7i",
 		"/ip4/138.201.68.74/tcp/4001/p2p/QmdnXwLrC8p1ueiq2Qya8joNvk3TVVDAut7PrikmZwubtR",
+		"/ip4/138.201.68.74/udp/4001/quic/p2p/QmdnXwLrC8p1ueiq2Qya8joNvk3TVVDAut7PrikmZwubtR",
 		"/ip4/94.130.135.167/tcp/4001/p2p/QmUEMvxS2e7iDrereVYc5SWPauXPyNwxcy9BXZrC1QTcHE",
+		"/ip4/94.130.135.167/udp/4001/quic/p2p/QmUEMvxS2e7iDrereVYc5SWPauXPyNwxcy9BXZrC1QTcHE",
 
 		// You can add more nodes here, for example, another IPFS node you might have running locally, mine was:
 		// "/ip4/127.0.0.1/tcp/4010/p2p/QmZp2fhDLxjYue2RiUvLwT9MWdnbDxam32qYFnGmxZDh5L",
+		// "/ip4/127.0.0.1/udp/4010/quic/p2p/QmZp2fhDLxjYue2RiUvLwT9MWdnbDxam32qYFnGmxZDh5L",
 	}
 
 	go connectToPeers(ctx, ipfs, bootstrapNodes)
diff --git a/docs/examples/library-experimental-features/README.md b/docs/examples/library-experimental-features/README.md
index 6387f3618a904635e330f6369c3924edb0f66cb1..a351706951b36e209682927e52d910ec058b087a 100644
--- a/docs/examples/library-experimental-features/README.md
+++ b/docs/examples/library-experimental-features/README.md
@@ -56,8 +56,6 @@ func createTempRepo(ctx context.Context) (string, error) {
 	cfg.Experimental.Libp2pStreamMounting = true
 	// https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#p2p-http-proxy
 	cfg.Experimental.P2pHttpProxy = true
-	// https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#quic
-	cfg.Experimental.QUIC = true
 	// https://github.com/ipfs/go-ipfs/blob/master/docs/experimental-features.md#strategic-providing
 	cfg.Experimental.StrategicProviding = true
 
diff --git a/docs/experimental-features.md b/docs/experimental-features.md
index d498653fcf39fe3275d1b340f6a719a788338a44..820fdc6ca3d8f75630c4e89c16524e669f6a69b8 100644
--- a/docs/experimental-features.md
+++ b/docs/experimental-features.md
@@ -22,7 +22,6 @@ the above issue.
 - [Plugins](#plugins)
 - [Directory Sharding / HAMT](#directory-sharding--hamt)
 - [IPNS PubSub](#ipns-pubsub)
-- [QUIC](#quic)
 - [AutoRelay](#autorelay)
 - [Strategic Providing](#strategic-providing)
 - [Graphsync](#graphsync)
@@ -463,35 +462,6 @@ run your daemon with the `--enable-namesys-pubsub` flag; enables pubsub.
 - [ ] Needs more people to use and report on how well it works
 - [ ] Pubsub enabled as a real feature
 
-## QUIC
-
-### In Version
-
-0.4.18
-
-### State
-
-Candidate, disabled by default but it will be enabled by default in 0.6.0.
-
-### How to enable
-
-Modify your ipfs config:
-
-```
-ipfs config --json Experimental.QUIC true
-```
-
-For listening on a QUIC address, add it to the swarm addresses, e.g. `/ip4/0.0.0.0/udp/4001/quic`.
-
-
-### Road to being a real feature
-
-- [ ] The IETF QUIC specification needs to be finalized.
-- [ ] Make sure QUIC connections work reliably
-- [ ] Make sure QUIC connection offer equal or better performance than TCP connections on real-world networks
-- [ ] Finalize libp2p-TLS handshake spec.
-
-
 ## AutoRelay
 
 ### In Version
diff --git a/docs/file-transfer.md b/docs/file-transfer.md
index 247993489ff0bce9d73201969f8b9871c4f8f051..8f38ca7653114e671db82b4ef9dfff4e650bf411 100644
--- a/docs/file-transfer.md
+++ b/docs/file-transfer.md
@@ -46,7 +46,9 @@ addresses (like the example below), then your nodes are online.
         "PublicKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZb6znj3LQZKP1+X81exf+vbnqNCMtHjZ5RKTCm7Fytnfe+AI1fhs9YbZdkgFkM1HLxmIOLQj2bMXPIGxUM+EnewN8tWurx4B3+lR/LWNwNYcCFL+jF2ltc6SE6BC8kMLEZd4zidOLPZ8lIRpd0x3qmsjhGefuRwrKeKlR4tQ3C76ziOms47uLdiVVkl5LyJ5+mn4rXOjNKt/oy2O4m1St7X7/yNt8qQgYsPfe/hCOywxCEIHEkqmil+vn7bu4RpAtsUzCcBDoLUIWuU3i6qfytD05hP8Clo+at+l//ctjMxylf3IQ5qyP+yfvazk+WHcsB0tWueEmiU5P2nfUUIR3AgMBAAE=",
         "Addresses": [
                 "/ip4/127.0.0.1/tcp/4001/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8",
+                "/ip4/127.0.0.1/udp/4001/quic/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8",
                 "/ip4/192.168.2.131/tcp/4001/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8",
+                "/ip4/192.168.2.131/udp/4001/quic/p2p/QmTNwsFkLAed15kQEC1ZJWPfoNbBQnMFojfJKQ9sZj1dk8",
         ],
         "AgentVersion": "go-ipfs/0.4.11-dev/",
         "ProtocolVersion": "ipfs/0.1.0"
@@ -90,8 +92,11 @@ Example output of addresses might look something like this:
 
 ```
 /ip4/127.0.0.1/tcp/4001
+/ip4/127.0.0.1/udp/4001/quic
 /ip4/192.168.2.133/tcp/4001
+/ip4/192.168.2.133/udp/4001/quic
 /ip4/88.157.217.196/tcp/63674
+/ip4/88.157.217.196/udp/63674/quic
 ```
 
 In this case, we can see a localhost (127.0.0.1) address, a LAN address (the
diff --git a/test/sharness/t0125-twonode.sh b/test/sharness/t0125-twonode.sh
index c70213ac7704928670bca73b34567e675bbd8108..424b610ff99a03ba2312590d5144e7d0bd4189ef 100755
--- a/test/sharness/t0125-twonode.sh
+++ b/test/sharness/t0125-twonode.sh
@@ -89,12 +89,6 @@ test_expect_success "set up tcp testbed" '
   iptb testbed create -type localipfs -count 2 -force -init
 '
 
-# Enable quic but don't use it yet.
-test_expect_success "enable QUIC experiment" '
-  ipfsi 0 config --json Experimental.QUIC true &&
-  ipfsi 1 config --json Experimental.QUIC true
-'
-
 # test multiplex muxer
 echo "Running advanced tests with mplex"
 export LIBP2P_MUX_PREFS="/mplex/6.7.0"
diff --git a/test/sharness/t0142-testfilter.sh b/test/sharness/t0142-testfilter.sh
index 0b46e2c9556f7bf2838b9d86d755ad32b0380ad2..971aa68397a2a8f1be2826736cbca1ea0b6ef02e 100755
--- a/test/sharness/t0142-testfilter.sh
+++ b/test/sharness/t0142-testfilter.sh
@@ -21,11 +21,6 @@ test_expect_success 'filter 127.0.0.0/24 on node 1' '
 '
 
 for i in $(seq 0 $(( NUM_NODES - 1 ))); do
-  test_expect_success 'enable quic for node $i' '
-    echo "$i"
-    ipfsi $i config --json Experimental.QUIC true
-  '
-
   test_expect_success "change IP for node $i" '
     ipfsi $i config --json "Addresses.Swarm" \
       "[\"/ip4/127.0.$i.1/tcp/0\",\"/ip4/127.0.$i.1/udp/0/quic\",\"/ip4/127.0.$i.1/tcp/0/ws\"]"
diff --git a/test/sharness/t0190-quic-ping.sh b/test/sharness/t0190-quic-ping.sh
index c16b23471f1a990f42f40aabea436afd8d88545e..ee0bf16ac19134563c60ad5058c5a3a6d1dc1087 100755
--- a/test/sharness/t0190-quic-ping.sh
+++ b/test/sharness/t0190-quic-ping.sh
@@ -11,11 +11,6 @@ test_expect_success 'init iptb' '
   iptb testbed create -type localipfs -count 2 -init
 '
 
-test_expect_success "enable QUIC experiment" '
-  ipfsi 0 config --json Experimental.QUIC true &&
-  ipfsi 1 config --json Experimental.QUIC true
-'
-
 addr1='"[\"/ip4/127.0.0.1/udp/0/quic/\"]"'
 addr2='"[\"/ip4/127.0.0.1/udp/0/quic/\"]"'
 test_expect_success "add QUIC swarm addresses" '