diff --git a/src/lib/docker-utils.ts b/src/lib/docker-utils.ts
index 65ec9c78..8842158b 100644
--- a/src/lib/docker-utils.ts
+++ b/src/lib/docker-utils.ts
@@ -1,6 +1,7 @@
 import { DockerProgress, ProgressCallback } from 'docker-progress';
 import * as Dockerode from 'dockerode';
 import * as _ from 'lodash';
+import * as memoizee from 'memoizee';
 
 import { applyDelta, OutOfSyncError } from 'docker-delta';
 import DockerToolbelt = require('docker-toolbelt');
@@ -27,6 +28,18 @@ interface RsyncApplyOptions {
 	retryInterval: number;
 }
 
+// TODO: Correctly export this from docker-toolbelt
+interface ImageNameParts {
+	registry: string;
+	imageName: string;
+	tagName: string;
+	digest: string;
+}
+
+// How long do we keep a delta token before invalidating it
+// (10 mins)
+const DELTA_TOKEN_TIMEOUT = 10 * 60 * 1000;
+
 export class DockerUtils extends DockerToolbelt {
 	public dockerProgress: DockerProgress;
 
@@ -90,27 +103,7 @@ export class DockerUtils extends DockerToolbelt {
 			this.getRegistryAndName(deltaOpts.deltaSource),
 		]);
 
-		const tokenEndpoint = `${deltaOpts.apiEndpoint}/auth/v1/token`;
-		const tokenOpts: requestLib.CoreOptions = {
-			auth: {
-				user: `d_${deltaOpts.uuid}`,
-				pass: deltaOpts.currentApiKey,
-				sendImmediately: true,
-			},
-			json: true,
-		};
-		const tokenUrl = `${tokenEndpoint}?service=${
-			dstInfo.registry
-		}&scope=repository:${dstInfo.imageName}:pull&scope=repository:${
-			srcInfo.imageName
-		}:pull`;
-
-		const tokenResponseBody = (await request.getAsync(tokenUrl, tokenOpts))[1];
-		const token = tokenResponseBody != null ? tokenResponseBody.token : null;
-
-		if (token == null) {
-			throw new ImageAuthenticationError('Authentication error');
-		}
+		const token = await this.getAuthToken(srcInfo, dstInfo, deltaOpts);
 
 		const opts: requestLib.CoreOptions = {
 			followRedirect: false,
@@ -324,6 +317,42 @@ export class DockerUtils extends DockerToolbelt {
 		await docker.dockerProgress.pull(deltaImg, onProgress, auth);
 		return (await docker.getImage(deltaImg).inspect()).Id;
 	}
+
+	private getAuthToken = memoizee(
+		async (
+			srcInfo: ImageNameParts,
+			dstInfo: ImageNameParts,
+			deltaOpts: DeltaFetchOptions,
+		): Promise<string> => {
+			const tokenEndpoint = `${deltaOpts.apiEndpoint}/auth/v1/token`;
+			const tokenOpts: requestLib.CoreOptions = {
+				auth: {
+					user: `d_${deltaOpts.uuid}`,
+					pass: deltaOpts.currentApiKey,
+					sendImmediately: true,
+				},
+				json: true,
+			};
+			const tokenUrl = `${tokenEndpoint}?service=${
+				dstInfo.registry
+			}&scope=repository:${dstInfo.imageName}:pull&scope=repository:${
+				srcInfo.imageName
+			}:pull`;
+
+			const tokenResponseBody = (await request.getAsync(
+				tokenUrl,
+				tokenOpts,
+			))[1];
+			const token = tokenResponseBody != null ? tokenResponseBody.token : null;
+
+			if (token == null) {
+				throw new ImageAuthenticationError('Authentication error');
+			}
+
+			return token;
+		},
+		{ maxAge: DELTA_TOKEN_TIMEOUT, promise: true },
+	);
 }
 
 export default DockerUtils;