diff --git a/src/main/java/org/torproject/collector/conf/Annotation.java b/src/main/java/org/torproject/collector/conf/Annotation.java
index 0dab5463ce31bf5651da7767a558447c69e0cb40..23f485c66acf0f4cd995211600ba45a0ca73cedc 100644
--- a/src/main/java/org/torproject/collector/conf/Annotation.java
+++ b/src/main/java/org/torproject/collector/conf/Annotation.java
@@ -16,7 +16,7 @@ public enum Annotation {
   Microdescriptor("@type microdescriptor 1.0\n"),
   Server("@type server-descriptor 1.0\n"),
   Status("@type bridge-network-status 1.2\n"),
-  OnionPerf("@type torperf 1.0\n"),
+  OnionPerf("@type torperf 1.1\n"),
   Vote("@type network-status-vote-3 1.0\n");
 
   private final String annotation;
diff --git a/src/main/java/org/torproject/collector/conf/Key.java b/src/main/java/org/torproject/collector/conf/Key.java
index 43414320bf3d57c045616fde9c9a85a2c926c47b..72af4bb1682b2147badc328de6f37a8daad46b96 100644
--- a/src/main/java/org/torproject/collector/conf/Key.java
+++ b/src/main/java/org/torproject/collector/conf/Key.java
@@ -34,6 +34,7 @@ public enum Key {
   BridgeSyncOrigins(URL[].class),
   BridgeLocalOrigins(Path.class),
   ExitlistSyncOrigins(URL[].class),
+  OnionPerfSyncOrigins(URL[].class),
   BridgedescsActivated(Boolean.class),
   BridgedescsOffsetMinutes(Integer.class),
   BridgedescsPeriodMinutes(Integer.class),
diff --git a/src/main/java/org/torproject/collector/onionperf/OnionPerfDownloader.java b/src/main/java/org/torproject/collector/onionperf/OnionPerfDownloader.java
index ccc809d8ad215a5045481481e8502a4fe94e8ffb..6069137ec330b560f34f7993cd2773d4684c9bc7 100644
--- a/src/main/java/org/torproject/collector/onionperf/OnionPerfDownloader.java
+++ b/src/main/java/org/torproject/collector/onionperf/OnionPerfDownloader.java
@@ -51,6 +51,7 @@ public class OnionPerfDownloader extends CollecTorMain {
   /** Instanciate the OnionPerf module using the given configuration. */
   public OnionPerfDownloader(Configuration config) {
     super(config);
+    this.mapPathDescriptors.put("recent/torperf", TorperfResult.class);
   }
 
   /** File containing the download history, which is necessary, because
diff --git a/src/main/java/org/torproject/collector/persist/OnionPerfPersistence.java b/src/main/java/org/torproject/collector/persist/OnionPerfPersistence.java
new file mode 100644
index 0000000000000000000000000000000000000000..22093ebb5296fab02a5336a90657c365fa99b423
--- /dev/null
+++ b/src/main/java/org/torproject/collector/persist/OnionPerfPersistence.java
@@ -0,0 +1,49 @@
+/* Copyright 2017 The Tor Project
+ * See LICENSE for licensing information */
+
+package org.torproject.collector.persist;
+
+import org.torproject.collector.conf.Annotation;
+import org.torproject.descriptor.TorperfResult;
+
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+
+public class OnionPerfPersistence
+    extends DescriptorPersistence<TorperfResult> {
+
+  private static final String ONIONPERF = "torperf";
+
+  public OnionPerfPersistence(TorperfResult desc) {
+    super(desc, Annotation.OnionPerf.bytes());
+    calculatePaths();
+  }
+
+  private void calculatePaths() {
+    String[] parts = PersistenceUtils.dateTimeParts(desc.getStartMillis());
+    String name =  desc.getSource() + DASH + desc.getFileSize() + DASH
+        + parts[0] + DASH + parts[1] + DASH + parts[2] + ".tpf";
+    this.recentPath = Paths.get(ONIONPERF, name).toString();
+    this.storagePath = Paths.get(
+        ONIONPERF,
+        parts[0], // year
+        parts[1], // month
+        parts[2], // day
+        name).toString();
+  }
+
+  /** OnionPerf default storage appends. */
+  @Override
+  public boolean storeOut(String outRoot) {
+    return super.storeOut(outRoot, StandardOpenOption.APPEND);
+  }
+
+  /** OnionPerf default storage appends. */
+  @Override
+  public boolean storeAll(String recentRoot, String outRoot) {
+    return super.storeAll(recentRoot, outRoot, StandardOpenOption.APPEND,
+        StandardOpenOption.APPEND);
+  }
+
+}
+
diff --git a/src/main/java/org/torproject/collector/sync/SyncPersistence.java b/src/main/java/org/torproject/collector/sync/SyncPersistence.java
index 5fd6dc626f3258a4110652bbb42deef0353ae614..baca83d19fc83042944915c6a8ce4e72ac73b242 100644
--- a/src/main/java/org/torproject/collector/sync/SyncPersistence.java
+++ b/src/main/java/org/torproject/collector/sync/SyncPersistence.java
@@ -13,6 +13,7 @@ import org.torproject.collector.persist.DescriptorPersistence;
 import org.torproject.collector.persist.ExitlistPersistence;
 import org.torproject.collector.persist.ExtraInfoPersistence;
 import org.torproject.collector.persist.MicroConsensusPersistence;
+import org.torproject.collector.persist.OnionPerfPersistence;
 import org.torproject.collector.persist.PersistenceUtils;
 import org.torproject.collector.persist.ServerDescriptorPersistence;
 import org.torproject.collector.persist.StatusPersistence;
@@ -26,6 +27,7 @@ import org.torproject.descriptor.RelayExtraInfoDescriptor;
 import org.torproject.descriptor.RelayNetworkStatusConsensus;
 import org.torproject.descriptor.RelayNetworkStatusVote;
 import org.torproject.descriptor.RelayServerDescriptor;
+import org.torproject.descriptor.TorperfResult;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -127,6 +129,9 @@ public class SyncPersistence {
         case "ExitList": // downloaded is part of desc, which to use?
           descPersist = new ExitlistPersistence((ExitList) desc, received);
           break;
+        case "TorperfResult":
+          descPersist = new OnionPerfPersistence((TorperfResult) desc);
+          break;
         default:
           log.trace("Invalid descriptor type {} for sync-merge.",
               clazz.getName());
diff --git a/src/main/resources/collector.properties b/src/main/resources/collector.properties
index d49bd58b9412798895cf7fc141982a3df8bd07ee..0a9f932c8d49bf543314757637df3014be36eff6 100644
--- a/src/main/resources/collector.properties
+++ b/src/main/resources/collector.properties
@@ -142,8 +142,11 @@ ExitlistUrl = https://check.torproject.org/exit-addresses
 ######## OnionPerf downloader ########
 #
 ## Define descriptor sources
-#  possible values: Remote
+#  possible values: Remote,Sync
 OnionPerfSources = Remote
+#  Retrieve files from the following CollecTor instances.
+#  List of URLs separated by comma.
+OnionPerfSyncOrigins = https://collector.torproject.org
 #
 ## OnionPerf base URLs
 ## Hosts must be configured to use the first subdomain part of the given URL as