diff --git a/docs/configuration.md b/docs/configuration.md
index a06553f473f684674ebc9322a348a47548de2232..fd9b7a91d613890d145fa320f92523410bad3f09 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -725,3 +725,30 @@ Obviously, you can't set repository or package file location with this method.
   <td>`RENOVATE_METEOR`</td>
   <td>`--meteor`<td>
 </tr>
+<tr>
+  <td>`docker`</td>
+  <td>Configuration object for Dockerfile renovation</td>
+  <td>json</td>
+  <td><pre>{
+  "enabled": false,
+  "branchName": "{{branchPrefix}}docker-{{depName}}-{{currentTag}}",
+  "commitMessage": "Update {{depName}}:{{currentTag}} digest",
+  "prTitle": "Update Dockerfile image {{depName}}@{{currentTag}} digest",
+  "prBody": "This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request updates Docker base image `{{depName}}@{{currentTag}}` to the latest digest (`{{newDigest}}`).\n\n{{#if schedule}}\n**Note**: This PR was created on a configured schedule (\"{{schedule}}\"{{#if timezone}} in timezone `{{timezone}}`{{/if}}) and will not receive updates outside those times.\n{{/if}}\n\n{{#if hasErrors}}\n\n---\n\n### Errors\n\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\n\n{{#each errors as |error|}}\n-   `{{error.depName}}`: {{error.message}}\n{{/each}}\n{{/if}}\n\n{{#if hasWarnings}}\n\n---\n\n### Warnings\n\nPlease make sure the following warnings are safe to ignore:\n\n{{#each warnings as |warning|}}\n-   `{{warning.depName}}`: {{warning.message}}\n{{/each}}\n{{/if}}\n\n---\n\nThis {{#if isGitHub}}PR{{else}}MR{{/if}} has been generated by [Renovate Bot](https://renovateapp.com).",
+  "pin": {
+    "prTitle": "Pin Dockerfile image {{depName}}@{{currentTag}} digest",
+    "prBody": "This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request pins Docker base image `{{depName}}@{{currentTag}}` to use a digest (`{{newDigest}}`).\nThis digest will then be kept updated via Pull Requests whenever the image is updated on the Docker registry.\n\n{{#if schedule}}\n**Note**: This PR was created on a configured schedule (\"{{schedule}}\"{{#if timezone}} in timezone `{{timezone}}`{{/if}}) and will not receive updates outside those times.\n{{/if}}\n\n{{#if hasErrors}}\n\n---\n\n### Errors\n\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\n\n{{#each errors as |error|}}\n-   `{{error.depName}}`: {{error.message}}\n{{/each}}\n{{/if}}\n\n{{#if hasWarnings}}\n\n---\n\n### Warnings\n\nPlease make sure the following warnings are safe to ignore:\n\n{{#each warnings as |warning|}}\n-   `{{warning.depName}}`: {{warning.message}}\n{{/each}}\n{{/if}}\n\n---\n\nThis {{#if isGitHub}}PR{{else}}MR{{/if}} has been generated by [Renovate Bot](https://renovateapp.com).",
+    "groupName": "Pin Docker Digests",
+    "group": {
+      "prTitle": "Pin Docker digests",
+      "prBody": "This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request pins Dockerfiles to use image digests.\n\n{{#if schedule}}\n**Note**: This PR was created on a configured schedule (\"{{schedule}}\"{{#if timezone}} in timezone `{{timezone}}`{{/if}}) and will not receive updates outside those times.\n{{/if}}\n\n{{#each upgrades as |upgrade|}}\n-   {{#if repositoryUrl}}[{{upgrade.depName}}]({{upgrade.repositoryUrl}}){{else}}`{{depName}}`{{/if}}: `{{upgrade.newDigest}}`\n{{/each}}\n\n{{#if hasErrors}}\n\n---\n\n### Errors\n\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\n\n{{#each errors as |error|}}\n-   `{{error.depName}}`: {{error.message}}\n{{/each}}\n{{/if}}\n\n{{#if hasWarnings}}\n\n---\n\n### Warnings\n\nPlease make sure the following warnings are safe to ignore:\n\n{{#each warnings as |warning|}}\n-   `{{warning.depName}}`: {{warning.message}}\n{{/each}}\n{{/if}}\n\n---\n\nThis {{#if isGitHub}}PR{{else}}MR{{/if}} has been generated by [Renovate Bot](https://renovateapp.com)."
+    }
+  },
+  "group": {
+    "prTitle": "Update Docker {{groupName}} digests",
+    "prBody": "This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request updates Dockerfiles to use image digests.\n\n{{#if schedule}}\n**Note**: This PR was created on a configured schedule (\"{{schedule}}\"{{#if timezone}} in timezone `{{timezone}}`{{/if}}) and will not receive updates outside those times.\n{{/if}}\n\n{{#each upgrades as |upgrade|}}\n-   {{#if repositoryUrl}}[{{upgrade.depName}}]({{upgrade.repositoryUrl}}){{else}}`{{depName}}`{{/if}}: `{{upgrade.newDigest}}`\n{{/each}}\n\n{{#if hasErrors}}\n\n---\n\n### Errors\n\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\n\n{{#each errors as |error|}}\n-   `{{error.depName}}`: {{error.message}}\n{{/each}}\n{{/if}}\n\n{{#if hasWarnings}}\n\n---\n\n### Warnings\n\nPlease make sure the following warnings are safe to ignore:\n\n{{#each warnings as |warning|}}\n-   `{{warning.depName}}`: {{warning.message}}\n{{/each}}\n{{/if}}\n\n---\n\nThis {{#if isGitHub}}PR{{else}}MR{{/if}} has been generated by [Renovate Bot](https://renovateapp.com)."
+  }
+}</pre></td>
+  <td>`RENOVATE_DOCKER`</td>
+  <td><td>
+</tr>
diff --git a/lib/api/docker.js b/lib/api/docker.js
new file mode 100644
index 0000000000000000000000000000000000000000..159b058d1ed21cffec43d0ae1920d0f799338578
--- /dev/null
+++ b/lib/api/docker.js
@@ -0,0 +1,33 @@
+const got = require('got');
+
+module.exports = {
+  getDigest,
+};
+
+async function getDigest(name, tag, logger) {
+  const repository = name.includes('/') ? name : `library/${name}`;
+  try {
+    const authUrl = `https://auth.docker.io/token?service=registry.docker.io&scope=repository:${repository}:pull`;
+    logger.debug(`Obtaining docker registry token for ${repository}`);
+    const token = (await got(authUrl, { json: true })).body.token;
+    if (!token) {
+      logger.warn('Failed to obtain docker registry token');
+      return null;
+    }
+    logger.debug('Got docker registry token');
+    const url = `https://index.docker.io/v2/${repository}/manifests/${tag ||
+      'latest'}`;
+    const headers = {
+      Authorization: `Bearer ${token}`,
+      Accept: 'application/vnd.docker.distribution.manifest.v2+json',
+    };
+    const digest = (await got(url, { json: true, headers })).headers[
+      'docker-content-digest'
+    ];
+    logger.debug({ digest }, 'Got docker digest');
+    return digest;
+  } catch (err) {
+    logger.warn({ err, name, tag }, 'Error getting docker image digest');
+    return null;
+  }
+}
diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index 887ea335467907f6d323b76b8567709011fea23b..3558d63993d0b0f3fde016b6286a4289e215a110 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -557,7 +557,6 @@ const options = [
     description: 'Requested reviewers for Pull Requests (GitHub only)',
     type: 'list',
   },
-  // meteor
   {
     name: 'meteor',
     description: 'Configuration object for meteor package.js renovation',
@@ -566,6 +565,34 @@ const options = [
     default: { enabled: false },
     mergeable: true,
   },
+  {
+    name: 'docker',
+    description: 'Configuration object for Dockerfile renovation',
+    stage: 'repository',
+    type: 'json',
+    default: {
+      enabled: false,
+      branchName: template('branchName', 'docker'),
+      commitMessage: template('commitMessage', 'docker'),
+      prTitle: template('prTitle', 'docker'),
+      prBody: template('prBody', 'docker'),
+      pin: {
+        prTitle: template('prTitle', 'docker-pin'),
+        prBody: template('prBody', 'docker-pin'),
+        groupName: 'Pin Docker Digests',
+        group: {
+          prTitle: template('prTitle', 'docker-pin-group'),
+          prBody: template('prBody', 'docker-pin-group'),
+        },
+      },
+      group: {
+        prTitle: template('prTitle', 'docker-group'),
+        prBody: template('prBody', 'docker-group'),
+      },
+    },
+    mergeable: true,
+    cli: false,
+  },
 ];
 
 function getOptions() {
diff --git a/lib/config/templates/docker-group/pr-body.hbs b/lib/config/templates/docker-group/pr-body.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..dd3c54f5f2ae234aabc54edd684640a73d77209e
--- /dev/null
+++ b/lib/config/templates/docker-group/pr-body.hbs
@@ -0,0 +1,39 @@
+This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request updates Dockerfiles to use image digests.
+
+{{#if schedule}}
+**Note**: This PR was created on a configured schedule ("{{schedule}}"{{#if timezone}} in timezone `{{timezone}}`{{/if}}) and will not receive updates outside those times.
+{{/if}}
+
+{{#each upgrades as |upgrade|}}
+-   {{#if repositoryUrl}}[{{upgrade.depName}}]({{upgrade.repositoryUrl}}){{else}}`{{depName}}`{{/if}}: `{{upgrade.newDigest}}`
+{{/each}}
+
+{{#if hasErrors}}
+
+---
+
+### Errors
+
+Renovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.
+
+{{#each errors as |error|}}
+-   `{{error.depName}}`: {{error.message}}
+{{/each}}
+{{/if}}
+
+{{#if hasWarnings}}
+
+---
+
+### Warnings
+
+Please make sure the following warnings are safe to ignore:
+
+{{#each warnings as |warning|}}
+-   `{{warning.depName}}`: {{warning.message}}
+{{/each}}
+{{/if}}
+
+---
+
+This {{#if isGitHub}}PR{{else}}MR{{/if}} has been generated by [Renovate Bot](https://renovateapp.com).
diff --git a/lib/config/templates/docker-group/pr-title.hbs b/lib/config/templates/docker-group/pr-title.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..b4feb91a674539c8db31b7748ea490f07af17c7e
--- /dev/null
+++ b/lib/config/templates/docker-group/pr-title.hbs
@@ -0,0 +1 @@
+Update Docker {{groupName}} digests
diff --git a/lib/config/templates/docker-pin-group/pr-body.hbs b/lib/config/templates/docker-pin-group/pr-body.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..8d8572f8f74291d16f14f57a456a496477f5d39c
--- /dev/null
+++ b/lib/config/templates/docker-pin-group/pr-body.hbs
@@ -0,0 +1,39 @@
+This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request pins Dockerfiles to use image digests.
+
+{{#if schedule}}
+**Note**: This PR was created on a configured schedule ("{{schedule}}"{{#if timezone}} in timezone `{{timezone}}`{{/if}}) and will not receive updates outside those times.
+{{/if}}
+
+{{#each upgrades as |upgrade|}}
+-   {{#if repositoryUrl}}[{{upgrade.depName}}]({{upgrade.repositoryUrl}}){{else}}`{{depName}}`{{/if}}: `{{upgrade.newDigest}}`
+{{/each}}
+
+{{#if hasErrors}}
+
+---
+
+### Errors
+
+Renovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.
+
+{{#each errors as |error|}}
+-   `{{error.depName}}`: {{error.message}}
+{{/each}}
+{{/if}}
+
+{{#if hasWarnings}}
+
+---
+
+### Warnings
+
+Please make sure the following warnings are safe to ignore:
+
+{{#each warnings as |warning|}}
+-   `{{warning.depName}}`: {{warning.message}}
+{{/each}}
+{{/if}}
+
+---
+
+This {{#if isGitHub}}PR{{else}}MR{{/if}} has been generated by [Renovate Bot](https://renovateapp.com).
diff --git a/lib/config/templates/docker-pin-group/pr-title.hbs b/lib/config/templates/docker-pin-group/pr-title.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..719292a0f94f5ec7b406ad8335aa2f6d1e3324bd
--- /dev/null
+++ b/lib/config/templates/docker-pin-group/pr-title.hbs
@@ -0,0 +1 @@
+Pin Docker digests
diff --git a/lib/config/templates/docker-pin/pr-body.hbs b/lib/config/templates/docker-pin/pr-body.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..5d22c990801af3786f255172ca5439bc89c43148
--- /dev/null
+++ b/lib/config/templates/docker-pin/pr-body.hbs
@@ -0,0 +1,36 @@
+This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request pins Docker base image `{{depName}}@{{currentTag}}` to use a digest (`{{newDigest}}`).
+This digest will then be kept updated via Pull Requests whenever the image is updated on the Docker registry.
+
+{{#if schedule}}
+**Note**: This PR was created on a configured schedule ("{{schedule}}"{{#if timezone}} in timezone `{{timezone}}`{{/if}}) and will not receive updates outside those times.
+{{/if}}
+
+{{#if hasErrors}}
+
+---
+
+### Errors
+
+Renovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.
+
+{{#each errors as |error|}}
+-   `{{error.depName}}`: {{error.message}}
+{{/each}}
+{{/if}}
+
+{{#if hasWarnings}}
+
+---
+
+### Warnings
+
+Please make sure the following warnings are safe to ignore:
+
+{{#each warnings as |warning|}}
+-   `{{warning.depName}}`: {{warning.message}}
+{{/each}}
+{{/if}}
+
+---
+
+This {{#if isGitHub}}PR{{else}}MR{{/if}} has been generated by [Renovate Bot](https://renovateapp.com).
diff --git a/lib/config/templates/docker-pin/pr-title.hbs b/lib/config/templates/docker-pin/pr-title.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..ba04e763863ff48ec5d93a9bad6d8fd92a657719
--- /dev/null
+++ b/lib/config/templates/docker-pin/pr-title.hbs
@@ -0,0 +1 @@
+Pin Dockerfile image {{depName}}@{{currentTag}} digest
diff --git a/lib/config/templates/docker/branch-name.hbs b/lib/config/templates/docker/branch-name.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..29bde578be3fddb582f0952378462db978094c4b
--- /dev/null
+++ b/lib/config/templates/docker/branch-name.hbs
@@ -0,0 +1 @@
+{{branchPrefix}}docker-{{depName}}-{{currentTag}}
diff --git a/lib/config/templates/docker/commit-message.hbs b/lib/config/templates/docker/commit-message.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..0f3d052939d594e72a93170cf42fdb1bb5adf112
--- /dev/null
+++ b/lib/config/templates/docker/commit-message.hbs
@@ -0,0 +1 @@
+Update {{depName}}:{{currentTag}} digest
diff --git a/lib/config/templates/docker/pr-body.hbs b/lib/config/templates/docker/pr-body.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..84718b30b2b7a296f11c24d03940b49e5055f2cd
--- /dev/null
+++ b/lib/config/templates/docker/pr-body.hbs
@@ -0,0 +1,35 @@
+This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request updates Docker base image `{{depName}}@{{currentTag}}` to the latest digest (`{{newDigest}}`).
+
+{{#if schedule}}
+**Note**: This PR was created on a configured schedule ("{{schedule}}"{{#if timezone}} in timezone `{{timezone}}`{{/if}}) and will not receive updates outside those times.
+{{/if}}
+
+{{#if hasErrors}}
+
+---
+
+### Errors
+
+Renovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.
+
+{{#each errors as |error|}}
+-   `{{error.depName}}`: {{error.message}}
+{{/each}}
+{{/if}}
+
+{{#if hasWarnings}}
+
+---
+
+### Warnings
+
+Please make sure the following warnings are safe to ignore:
+
+{{#each warnings as |warning|}}
+-   `{{warning.depName}}`: {{warning.message}}
+{{/each}}
+{{/if}}
+
+---
+
+This {{#if isGitHub}}PR{{else}}MR{{/if}} has been generated by [Renovate Bot](https://renovateapp.com).
diff --git a/lib/config/templates/docker/pr-title.hbs b/lib/config/templates/docker/pr-title.hbs
new file mode 100644
index 0000000000000000000000000000000000000000..9924b03b5856a83ea4be6cb342c6c3258cd9e9ec
--- /dev/null
+++ b/lib/config/templates/docker/pr-title.hbs
@@ -0,0 +1 @@
+Update Dockerfile image {{depName}}@{{currentTag}} digest
diff --git a/lib/workers/branch/dockerfile.js b/lib/workers/branch/dockerfile.js
new file mode 100644
index 0000000000000000000000000000000000000000..52bb16f5106d012eb5e170bc2f8b4fb1716c5b1c
--- /dev/null
+++ b/lib/workers/branch/dockerfile.js
@@ -0,0 +1,19 @@
+module.exports = {
+  setNewValue,
+};
+
+function setNewValue(
+  currentFileContent,
+  depName,
+  currentVersion,
+  newVersion,
+  logger
+) {
+  logger.debug(`setNewValue: ${depName} = ${newVersion}`);
+  const regexReplace = new RegExp(`(^|\n)FROM ${currentVersion}\n`);
+  const newFileContent = currentFileContent.replace(
+    regexReplace,
+    `$1FROM ${newVersion}\n`
+  );
+  return newFileContent;
+}
diff --git a/lib/workers/branch/package-files.js b/lib/workers/branch/package-files.js
index 1620614db1fe3c1e3e6b0c28b05916969d3e0e76..523dd239b2afee366e6d1cafa113496d611475d6 100644
--- a/lib/workers/branch/package-files.js
+++ b/lib/workers/branch/package-files.js
@@ -1,5 +1,6 @@
 const packageJsonHelper = require('./package-json');
 const packageJsHelper = require('./package-js');
+const dockerfileHelper = require('./dockerfile');
 
 module.exports = {
   getUpdatedPackageFiles,
@@ -17,7 +18,7 @@ async function getUpdatedPackageFiles(config) {
           upgrade.packageFile,
           config.parentBranch
         ));
-      let newContent;
+      let newContent = existingContent;
       if (upgrade.packageFile.endsWith('package.json')) {
         newContent = packageJsonHelper.setNewValue(
           existingContent,
@@ -26,7 +27,7 @@ async function getUpdatedPackageFiles(config) {
           upgrade.newVersion,
           config.logger
         );
-      } else {
+      } else if (upgrade.packageFile.endsWith('package.js')) {
         newContent = packageJsHelper.setNewValue(
           existingContent,
           upgrade.depName,
@@ -34,6 +35,14 @@ async function getUpdatedPackageFiles(config) {
           upgrade.newVersion,
           config.logger
         );
+      } else if (upgrade.packageFile.endsWith('Dockerfile')) {
+        newContent = dockerfileHelper.setNewValue(
+          existingContent,
+          upgrade.depName,
+          upgrade.currentFrom,
+          upgrade.newFrom,
+          config.logger
+        );
       }
       if (newContent !== existingContent) {
         logger.debug('Updating packageFile content');
diff --git a/lib/workers/dep-type/index.js b/lib/workers/dep-type/index.js
index a7ff97aef2eab628b91da0ee9aaba24316358472..b1178a093e0da3e8f8d397b94ed81e3e0dfc6f84 100644
--- a/lib/workers/dep-type/index.js
+++ b/lib/workers/dep-type/index.js
@@ -15,15 +15,18 @@ async function renovateDepType(packageContent, config) {
     logger.debug('depType is disabled');
     return [];
   }
-  let deps;
+  let deps = [];
   if (config.packageFile.endsWith('package.json')) {
     // Extract all dependencies from the package.json
     deps = await packageJson.extractDependencies(
       packageContent,
       config.depType
     );
-    logger.debug(`currentDeps length is ${deps.length}`);
-    logger.debug({ deps }, `currentDeps`);
+    deps = deps.filter(
+      dependency => config.monorepoPackages.indexOf(dependency.depName) === -1
+    );
+    logger.debug(`deps length is ${deps.length}`);
+    logger.debug({ deps }, `deps`);
   } else if (config.packageFile.endsWith('package.js')) {
     deps = packageContent
       .match(/Npm\.depends\({([\s\S]*?)}\);/)[1]
@@ -35,11 +38,21 @@ async function renovateDepType(packageContent, config) {
         depName: arr[0],
         currentVersion: arr[1],
       }));
+  } else if (config.packageFile.endsWith('Dockerfile')) {
+    const [imagetag, currentDigest] = config.currentFrom.split('@');
+    const [depName, currentTag] = imagetag.split(':');
+    logger.info({ depName, currentTag, currentDigest }, 'Dockerfile');
+    deps = [
+      {
+        depType: 'docker',
+        depName,
+        currentTag: currentTag || 'latest',
+        currentDigest,
+      },
+    ];
   }
   deps = deps.filter(
-    dependency =>
-      config.ignoreDeps.indexOf(dependency.depName) === -1 &&
-      config.monorepoPackages.indexOf(dependency.depName) === -1
+    dependency => config.ignoreDeps.indexOf(dependency.depName) === -1
   );
   logger.debug(`filtered deps length is ${deps.length}`);
   logger.debug({ deps }, `filtered deps`);
diff --git a/lib/workers/package-file/index.js b/lib/workers/package-file/index.js
index 33bc37f64ce84a925a41d666bc57fc0d6ea64437..0d4f967230c061c48b8d6d5f80543e3647359df5 100644
--- a/lib/workers/package-file/index.js
+++ b/lib/workers/package-file/index.js
@@ -7,6 +7,7 @@ let logger = require('../../logger');
 module.exports = {
   renovatePackageFile,
   renovateMeteorPackageFile,
+  renovateDockerfile,
 };
 
 async function renovatePackageFile(packageFileConfig) {
@@ -91,3 +92,23 @@ async function renovateMeteorPackageFile(packageFileConfig) {
   logger.info('Finished processing package file');
   return upgrades;
 }
+
+async function renovateDockerfile(packageFileConfig) {
+  let upgrades = [];
+  logger = packageFileConfig.logger;
+  logger.info(`Processing Dockerfile`);
+
+  // Check if config is disabled
+  if (packageFileConfig.enabled === false) {
+    logger.info('Dockerfile is disabled');
+    return upgrades;
+  }
+  upgrades = upgrades.concat(
+    await depTypeWorker.renovateDepType(
+      packageFileConfig.content,
+      packageFileConfig
+    )
+  );
+  logger.info('Finished processing Dockerfile');
+  return upgrades;
+}
diff --git a/lib/workers/package/docker.js b/lib/workers/package/docker.js
new file mode 100644
index 0000000000000000000000000000000000000000..fb1e070a85ec760f3082ebeb79d461f8ed3bb404
--- /dev/null
+++ b/lib/workers/package/docker.js
@@ -0,0 +1,32 @@
+const dockerApi = require('../../api/docker');
+
+module.exports = {
+  renovateDockerImage,
+};
+
+async function renovateDockerImage(config) {
+  const newDigest = await dockerApi.getDigest(
+    config.depName,
+    config.currentTag,
+    config.logger
+  );
+  if (!newDigest || config.currentDigest === newDigest) {
+    return [];
+  }
+  const upgrade = {};
+  upgrade.newTag = config.currentTag;
+  upgrade.newDigest = newDigest;
+  upgrade.newFrom = config.depName;
+  if (upgrade.newTag) {
+    upgrade.newFrom += `:${upgrade.newTag}`;
+  }
+  upgrade.newFrom += `@${upgrade.newDigest}`;
+  if (config.currentDigest) {
+    upgrade.type = 'digest';
+    upgrade.isDigest = true;
+  } else {
+    upgrade.type = 'pin';
+    upgrade.isPin = true;
+  }
+  return [upgrade];
+}
diff --git a/lib/workers/package/index.js b/lib/workers/package/index.js
index d3f73de10dfeff290534902f5731fbe000762bae..bedf0875806ed311ec71c4671003d066677753a0 100644
--- a/lib/workers/package/index.js
+++ b/lib/workers/package/index.js
@@ -1,4 +1,5 @@
 const configParser = require('../../config');
+const { renovateDockerImage } = require('./docker');
 const { renovateNpmPackage } = require('./npm');
 
 module.exports = {
@@ -13,8 +14,13 @@ async function renovatePackage(config) {
     logger.debug('package is disabled');
     return [];
   }
-  // npm
-  const results = await renovateNpmPackage(config);
+  let results;
+  if (config.depType === 'docker') {
+    results = await renovateDockerImage(config);
+  } else {
+    // npm
+    results = await renovateNpmPackage(config);
+  }
   logger.debug({ results }, `${config.depName} lookup results`);
   // Flatten the result on top of config, add repositoryUrl
   return results.map(result => {
diff --git a/lib/workers/repository/apis.js b/lib/workers/repository/apis.js
index 662df87e40f57b58755c75d531f4104390243fa7..44e57c0e874b07e49c8a09c434eb07076a19578e 100644
--- a/lib/workers/repository/apis.js
+++ b/lib/workers/repository/apis.js
@@ -278,6 +278,11 @@ async function detectPackageFiles(input) {
     logger.info(`Found ${meteorPackageFiles.length} meteor package files`);
     config.packageFiles = config.packageFiles.concat(meteorPackageFiles);
   }
+  if (config.docker.enabled) {
+    const dockerFiles = await config.api.findFilePaths('Dockerfile');
+    logger.info(`Found ${dockerFiles.length} Dockerfiles`);
+    config.packageFiles = config.packageFiles.concat(dockerFiles);
+  }
   return config;
 }
 
@@ -396,6 +401,25 @@ async function resolvePackageFiles(inputConfig) {
     } else if (packageFile.packageFile.endsWith('package.js')) {
       // meteor
       packageFile = configParser.mergeChildConfig(config.meteor, packageFile);
+    } else if (packageFile.packageFile.endsWith('Dockerfile')) {
+      // docker
+      packageFile = configParser.mergeChildConfig(config.docker, packageFile);
+      logger.debug(`Resolving packageFile ${JSON.stringify(packageFile)}`);
+      packageFile.content = await config.api.getFileContent(
+        packageFile.packageFile,
+        config.baseBranch
+      );
+      const strippedComment = packageFile.content.replace(/^(#.*?\n)+/, '');
+      const fromMatch = strippedComment.match(/^FROM (.*)\n/);
+      if (!fromMatch) {
+        logger.debug(
+          { content: packageFile.content, strippedComment },
+          'No FROM found'
+        );
+        continue; // eslint-disable-line
+      }
+      packageFile.currentFrom = fromMatch[1];
+      logger.debug('Adding Dockerfile');
     }
 
     packageFiles.push(packageFile);
diff --git a/lib/workers/repository/index.js b/lib/workers/repository/index.js
index 053f6588e09f2f39b211af41e05d67090292a4de..4d3466d218d9f2ece414ad48f1ba67762f291186 100644
--- a/lib/workers/repository/index.js
+++ b/lib/workers/repository/index.js
@@ -69,7 +69,7 @@ async function renovateRepository(repoConfig, token) {
         config = await apis.detectPackageFiles(config);
         // If we can't detect any package.json then return
         if (config.packageFiles.length === 0) {
-          logger.info('Cannot detect package.json');
+          logger.info('Cannot detect package files');
           return;
         }
         logger.debug(
diff --git a/lib/workers/repository/upgrades.js b/lib/workers/repository/upgrades.js
index 428402c14adad5680eaa47e3887b188aab11a3bd..946b35176341b3a868ab0a23c859dc62ef8dc300 100644
--- a/lib/workers/repository/upgrades.js
+++ b/lib/workers/repository/upgrades.js
@@ -34,6 +34,11 @@ async function determineRepoUpgrades(config) {
       upgrades = upgrades.concat(
         await packageFileWorker.renovateMeteorPackageFile(packageFileConfig)
       );
+    } else if (packageFileConfig.packageFile.endsWith('Dockerfile')) {
+      logger.info('Renovating Dockerfile FROM');
+      upgrades = upgrades.concat(
+        await packageFileWorker.renovateDockerfile(packageFileConfig)
+      );
     }
   }
   return upgrades;
diff --git a/test/api/docker.spec.js b/test/api/docker.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..2ee71546629ddb44e08a205e4e9cc9cae1151e06
--- /dev/null
+++ b/test/api/docker.spec.js
@@ -0,0 +1,39 @@
+const docker = require('../../lib/api/docker');
+const got = require('got');
+const logger = require('../_fixtures/logger');
+
+jest.mock('got');
+
+describe('api/docker', () => {
+  describe('getDigest', () => {
+    beforeEach(() => {
+      jest.resetAllMocks();
+    });
+    it('returns null if no token', async () => {
+      got.mockReturnValueOnce({ body: {} });
+      const res = await docker.getDigest('some-name', undefined, logger);
+      expect(res).toBe(null);
+    });
+    it('returns null if errored', async () => {
+      got.mockReturnValueOnce({ body: { token: 'some-token' } });
+      const res = await docker.getDigest('some-name', undefined, logger);
+      expect(res).toBe(null);
+    });
+    it('returns digest', async () => {
+      got.mockReturnValueOnce({ body: { token: 'some-token' } });
+      got.mockReturnValueOnce({
+        headers: { 'docker-content-digest': 'some-digest' },
+      });
+      const res = await docker.getDigest('some-name', undefined, logger);
+      expect(res).toBe('some-digest');
+    });
+    it('supports scoped names', async () => {
+      got.mockReturnValueOnce({ body: { token: 'some-token' } });
+      got.mockReturnValueOnce({
+        headers: { 'docker-content-digest': 'some-digest' },
+      });
+      const res = await docker.getDigest('some/name', undefined, logger);
+      expect(res).toBe('some-digest');
+    });
+  });
+});
diff --git a/test/workers/branch/__snapshots__/dockerfile.spec.js.snap b/test/workers/branch/__snapshots__/dockerfile.spec.js.snap
new file mode 100644
index 0000000000000000000000000000000000000000..f69760bb1b822e3d705af42b0bf678f8235ad95c
--- /dev/null
+++ b/test/workers/branch/__snapshots__/dockerfile.spec.js.snap
@@ -0,0 +1,8 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`workers/branch/dockerfile setNewValue replaces existing value 1`] = `
+"# comment FROM node:8
+FROM node:8@sha256:abcdefghijklmnop
+RUN something
+"
+`;
diff --git a/test/workers/branch/dockerfile.spec.js b/test/workers/branch/dockerfile.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..424a6d8547a1b653f7854217b44ed70dfe9dfffe
--- /dev/null
+++ b/test/workers/branch/dockerfile.spec.js
@@ -0,0 +1,22 @@
+const dockerfile = require('../../../lib/workers/branch/dockerfile');
+const logger = require('../../_fixtures/logger');
+
+describe('workers/branch/dockerfile', () => {
+  describe('setNewValue', () => {
+    it('replaces existing value', () => {
+      const currentFileContent =
+        '# comment FROM node:8\nFROM node:8\nRUN something\n';
+      const depName = 'node';
+      const currentVersion = 'node:8';
+      const newVersion = 'node:8@sha256:abcdefghijklmnop';
+      const res = dockerfile.setNewValue(
+        currentFileContent,
+        depName,
+        currentVersion,
+        newVersion,
+        logger
+      );
+      expect(res).toMatchSnapshot();
+    });
+  });
+});
diff --git a/test/workers/branch/package-files.spec.js b/test/workers/branch/package-files.spec.js
index 60488b8aeb23653302a3da98a0fd08a0dacdd74c..72db0c10f069f2dd81b73cff61cb0014085ec9b7 100644
--- a/test/workers/branch/package-files.spec.js
+++ b/test/workers/branch/package-files.spec.js
@@ -1,5 +1,6 @@
 const packageJsonHelper = require('../../../lib/workers/branch/package-json');
 const packageJsHelper = require('../../../lib/workers/branch/package-js');
+const dockerHelper = require('../../../lib/workers/branch/dockerfile');
 const {
   getUpdatedPackageFiles,
 } = require('../../../lib/workers/branch/package-files');
@@ -16,6 +17,7 @@ describe('workers/branch/package-files', () => {
         logger,
       };
       packageJsonHelper.setNewValue = jest.fn();
+      dockerHelper.setNewValue = jest.fn();
       packageJsHelper.setNewValue = jest.fn();
     });
     it('returns empty if lock file maintenance', async () => {
@@ -26,14 +28,14 @@ describe('workers/branch/package-files', () => {
     it('returns updated files', async () => {
       config.upgrades = [
         { packageFile: 'package.json' },
-        { packageFile: 'backend/package.json' },
+        { packageFile: 'Dockerfile' },
         { packageFile: 'packages/foo/package.js' },
       ];
       config.api.getFileContent.mockReturnValueOnce('old content 1');
       config.api.getFileContent.mockReturnValueOnce('old content 2');
       config.api.getFileContent.mockReturnValueOnce('old content 3');
       packageJsonHelper.setNewValue.mockReturnValueOnce('old content 1');
-      packageJsonHelper.setNewValue.mockReturnValueOnce('new content 2');
+      dockerHelper.setNewValue.mockReturnValueOnce('new content 2');
       packageJsHelper.setNewValue.mockReturnValueOnce('old content 3');
       const res = await getUpdatedPackageFiles(config);
       expect(res).toHaveLength(1);
diff --git a/test/workers/dep-type/index.spec.js b/test/workers/dep-type/index.spec.js
index 025e19c862abacaf6e483250dadc1a46c36eb7ff..964fa64424b51cf5b3846d5992c2da93cd73463a 100644
--- a/test/workers/dep-type/index.spec.js
+++ b/test/workers/dep-type/index.spec.js
@@ -58,6 +58,12 @@ describe('lib/workers/dep-type/index', () => {
       const res = await depTypeWorker.renovateDepType(content, config);
       expect(res).toHaveLength(6);
     });
+    it('returns upgrades for docker', async () => {
+      config.packageFile = 'Dockerfile';
+      config.currentFrom = 'node';
+      const res = await depTypeWorker.renovateDepType('some-content', config);
+      expect(res).toHaveLength(1);
+    });
   });
   describe('getDepConfig(depTypeConfig, dep)', () => {
     const depTypeConfig = {
diff --git a/test/workers/package-file/index.spec.js b/test/workers/package-file/index.spec.js
index 23de2716c0374a498219e458c26df6323a4245b3..1cc19d4669adbb228fb8f3bc5aa96520a6c4ad0e 100644
--- a/test/workers/package-file/index.spec.js
+++ b/test/workers/package-file/index.spec.js
@@ -65,4 +65,26 @@ describe('packageFileWorker', () => {
       expect(res).toHaveLength(2);
     });
   });
+  describe('renovateDockerfile', () => {
+    let config;
+    beforeEach(() => {
+      config = {
+        ...defaultConfig,
+        packageFile: 'Dockerfile',
+        repoIsOnboarded: true,
+        logger,
+      };
+      depTypeWorker.renovateDepType.mockReturnValue([]);
+    });
+    it('returns empty if disabled', async () => {
+      config.enabled = false;
+      const res = await packageFileWorker.renovateDockerfile(config);
+      expect(res).toEqual([]);
+    });
+    it('returns upgrades', async () => {
+      depTypeWorker.renovateDepType.mockReturnValueOnce([{}, {}]);
+      const res = await packageFileWorker.renovateDockerfile(config);
+      expect(res).toHaveLength(2);
+    });
+  });
 });
diff --git a/test/workers/package/docker.spec.js b/test/workers/package/docker.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..0db380b7ed59f1a09ea5e7b7e71e88759d8d1b46
--- /dev/null
+++ b/test/workers/package/docker.spec.js
@@ -0,0 +1,42 @@
+const dockerApi = require('../../../lib/api/docker');
+const docker = require('../../../lib/workers/package/docker');
+const defaultConfig = require('../../../lib/config/defaults').getConfig();
+const logger = require('../../_fixtures/logger');
+
+// jest.mock('../../../lib/api/docker');
+dockerApi.getDigest = jest.fn();
+
+describe('lib/workers/package/docker', () => {
+  describe('renovateDockerImage', () => {
+    let config;
+    beforeEach(() => {
+      config = {
+        ...defaultConfig,
+        logger,
+        depName: 'some-dep',
+        currentTag: '1.0.0',
+        currentDigest: 'sha256:abcdefghijklmnop',
+      };
+    });
+    it('returns empty if no digest', async () => {
+      expect(await docker.renovateDockerImage(config)).toEqual([]);
+    });
+    it('returns empty if digest is same', async () => {
+      dockerApi.getDigest.mockReturnValueOnce(config.currentDigest);
+      expect(await docker.renovateDockerImage(config)).toEqual([]);
+    });
+    it('returns a digest', async () => {
+      dockerApi.getDigest.mockReturnValueOnce('sha256:1234567890');
+      const res = await docker.renovateDockerImage(config);
+      expect(res).toHaveLength(1);
+      expect(res[0].type).toEqual('digest');
+    });
+    it('returns a pin', async () => {
+      delete config.currentDigest;
+      dockerApi.getDigest.mockReturnValueOnce('sha256:1234567890');
+      const res = await docker.renovateDockerImage(config);
+      expect(res).toHaveLength(1);
+      expect(res[0].type).toEqual('pin');
+    });
+  });
+});
diff --git a/test/workers/package/index.spec.js b/test/workers/package/index.spec.js
index 7f4b25492c0329d6c7b45a374121c4b7a3a37048..6dd95485dd7d046d1e488efecaf203a702a7332d 100644
--- a/test/workers/package/index.spec.js
+++ b/test/workers/package/index.spec.js
@@ -2,8 +2,10 @@ const pkgWorker = require('../../../lib/workers/package/index');
 const defaultConfig = require('../../../lib/config/defaults').getConfig();
 const configParser = require('../../../lib/config');
 const logger = require('../../_fixtures/logger');
+const docker = require('../../../lib/workers/package/docker');
 const npm = require('../../../lib/workers/package/npm');
 
+jest.mock('../../../lib/workers/package/docker');
 jest.mock('../../../lib/workers/package/npm');
 
 describe('lib/workers/package/index', () => {
@@ -20,6 +22,12 @@ describe('lib/workers/package/index', () => {
       const res = await pkgWorker.renovatePackage(config);
       expect(res).toMatchObject([]);
     });
+    it('calls docker', async () => {
+      docker.renovateDockerImage.mockReturnValueOnce([]);
+      config.depType = 'docker';
+      const res = await pkgWorker.renovatePackage(config);
+      expect(res).toMatchObject([]);
+    });
     it('calls npm', async () => {
       npm.renovateNpmPackage.mockReturnValueOnce([]);
       config.depType = 'npm';
diff --git a/test/workers/repository/__snapshots__/apis.spec.js.snap b/test/workers/repository/__snapshots__/apis.spec.js.snap
index d21d5fc421e9928003d7a9f3934a2d3fe5b05943..add163b03e078922f182189f41fa3ba6927d518e 100644
--- a/test/workers/repository/__snapshots__/apis.spec.js.snap
+++ b/test/workers/repository/__snapshots__/apis.spec.js.snap
@@ -34,6 +34,13 @@ Array [
 ]
 `;
 
+exports[`workers/repository/apis detectPackageFiles(config) finds Dockerfiles 1`] = `
+Array [
+  "package.json",
+  "Dockerfile",
+]
+`;
+
 exports[`workers/repository/apis detectPackageFiles(config) finds meteor package files 1`] = `
 Array [
   "package.json",
diff --git a/test/workers/repository/apis.spec.js b/test/workers/repository/apis.spec.js
index b8a04c5699bc2e1c080c88bb0275bb5c3f0a59be..6b0b2e0b4e2ab54ed8982d1d79d05dddad97f5fc 100644
--- a/test/workers/repository/apis.spec.js
+++ b/test/workers/repository/apis.spec.js
@@ -216,6 +216,7 @@ describe('workers/repository/apis', () => {
   describe('detectPackageFiles(config)', () => {
     it('adds package files to object', async () => {
       const config = {
+        ...defaultConfig,
         api: {
           findFilePaths: jest.fn(() => [
             'package.json',
@@ -229,11 +230,11 @@ describe('workers/repository/apis', () => {
         warnings: [],
       };
       const res = await apis.detectPackageFiles(config);
-      expect(res).toMatchObject(config);
       expect(res.packageFiles).toMatchSnapshot();
     });
     it('finds meteor package files', async () => {
       const config = {
+        ...defaultConfig,
         api: {
           findFilePaths: jest.fn(),
         },
@@ -248,7 +249,23 @@ describe('workers/repository/apis', () => {
         'modules/something/package.js',
       ]);
       const res = await apis.detectPackageFiles(config);
-      expect(res).toMatchObject(config);
+      expect(res.packageFiles).toMatchSnapshot();
+    });
+    it('finds Dockerfiles', async () => {
+      const config = {
+        ...defaultConfig,
+        api: {
+          findFilePaths: jest.fn(),
+        },
+        docker: {
+          enabled: true,
+        },
+        logger,
+        warnings: [],
+      };
+      config.api.findFilePaths.mockReturnValueOnce(['package.json']);
+      config.api.findFilePaths.mockReturnValueOnce(['Dockerfile']);
+      const res = await apis.detectPackageFiles(config);
       expect(res.packageFiles).toMatchSnapshot();
     });
     it('ignores node modules', async () => {
@@ -328,6 +345,22 @@ describe('workers/repository/apis', () => {
       expect(res.packageFiles).toHaveLength(3);
       expect(res.packageFiles).toMatchSnapshot();
     });
+    it('handles dockerfile', async () => {
+      config.packageFiles = [{ packageFile: 'Dockerfile' }];
+      config.api.getFileContent.mockReturnValueOnce(
+        '# some content\nFROM node:8\nRUN something'
+      );
+      const res = await apis.resolvePackageFiles(config);
+      expect(res.packageFiles).toHaveLength(1);
+    });
+    it('handles dockerfile with no FROM', async () => {
+      config.packageFiles = [{ packageFile: 'Dockerfile' }];
+      config.api.getFileContent.mockReturnValueOnce(
+        '# some content\n# FROM node:8\nRUN something'
+      );
+      const res = await apis.resolvePackageFiles(config);
+      expect(res.packageFiles).toHaveLength(0);
+    });
   });
 });
 describe('migrateAndValidate', () => {
diff --git a/test/workers/repository/upgrades.spec.js b/test/workers/repository/upgrades.spec.js
index 0adbcc7553f0e070225e5096fcf162fdf370bd96..fcd9495e9e80957b79b8d9e9bce91754c958891b 100644
--- a/test/workers/repository/upgrades.spec.js
+++ b/test/workers/repository/upgrades.spec.js
@@ -30,7 +30,7 @@ describe('workers/repository/upgrades', () => {
     });
     it('returns array if upgrades found', async () => {
       config.packageFiles = [
-        'package.json',
+        'Dockerfile',
         {
           packageFile: 'backend/package.json',
         },
@@ -38,7 +38,7 @@ describe('workers/repository/upgrades', () => {
           packageFile: 'frontend/package.js',
         },
       ];
-      packageFileWorker.renovatePackageFile.mockReturnValueOnce(['a']);
+      packageFileWorker.renovateDockerfile.mockReturnValueOnce(['a']);
       packageFileWorker.renovatePackageFile.mockReturnValueOnce(['b', 'c']);
       packageFileWorker.renovateMeteorPackageFile.mockReturnValueOnce(['d']);
       const res = await upgrades.determineRepoUpgrades(config);