diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index dc1912d360eae14bb20b6a33117ddb045118055b..a8ea74ea3a5af0bcce5f6639242209463e3c0da7 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -1599,7 +1599,7 @@ Behavior:
 - `bump` = e.g. bump the range even if the new version satisfies the existing range, e.g. `^1.0.0` -> `^1.1.0`
 - `replace` = Replace the range with a newer one if the new version falls outside it, e.g. `^1.0.0` -> `^2.0.0`
 - `widen` = Widen the range with newer one, e.g. `^1.0.0` -> `^1.0.0 || ^2.0.0`
-- `update-lockfile` = Update the lock file when in-range updates are available, otherwise `replace` for updates out of range. Works for `bundler`, `composer`, `npm`, and `yarn`, so far
+- `update-lockfile` = Update the lock file when in-range updates are available, otherwise `replace` for updates out of range. Works for `bundler`, `composer`, `npm`, `yarn` and `poetry` so far
 
 Renovate's `"auto"` strategy works like this for npm:
 
diff --git a/lib/manager/poetry/__fixtures__/pyproject.11.toml b/lib/manager/poetry/__fixtures__/pyproject.11.toml
new file mode 100644
index 0000000000000000000000000000000000000000..bf71d8e580bf5222bd3e6c68190d482822551c47
--- /dev/null
+++ b/lib/manager/poetry/__fixtures__/pyproject.11.toml
@@ -0,0 +1,3 @@
+[tool.poetry.dependencies]
+python = "^3.9"
+boto3 = "*"
diff --git a/lib/manager/poetry/__fixtures__/pyproject.11.toml.lock b/lib/manager/poetry/__fixtures__/pyproject.11.toml.lock
new file mode 100644
index 0000000000000000000000000000000000000000..5768c71a3d674886e9065a8f03dd777770426b81
--- /dev/null
+++ b/lib/manager/poetry/__fixtures__/pyproject.11.toml.lock
@@ -0,0 +1,76 @@
+[[package]]
+name = "boto3"
+version = "1.17.5"
+description = "The AWS SDK for Python"
+category = "main"
+optional = false
+python-versions = ">= 2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[package.dependencies]
+botocore = ">=1.20.7,<1.21.0"
+jmespath = ">=0.7.1,<1.0.0"
+s3transfer = ">=0.3.0,<0.4.0"
+
+[[package]]
+name = "botocore"
+version = "1.20.7"
+description = "Low-level, data-driven core of boto 3."
+category = "main"
+optional = false
+python-versions = ">= 2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+
+[package.dependencies]
+jmespath = ">=0.7.1,<1.0.0"
+python-dateutil = ">=2.1,<3.0.0"
+urllib3 = ">=1.25.4,<1.27"
+
+[[package]]
+name = "jmespath"
+version = "0.10.0"
+description = "JSON Matching Expressions"
+category = "main"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "python-dateutil"
+version = "2.8.1"
+description = "Extensions to the standard Python datetime module"
+category = "main"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "s3transfer"
+version = "0.3.4"
+description = "An Amazon S3 Transfer Manager"
+category = "main"
+optional = false
+python-versions = "*"
+
+[package.dependencies]
+botocore = ">=1.12.36,<2.0a.0"
+
+[[package]]
+name = "six"
+version = "1.15.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "urllib3"
+version = "1.26.3"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+category = "main"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
+
+[package.extras]
+brotli = ["brotlipy (>=0.6.0)"]
+secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
+socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
diff --git a/lib/manager/poetry/__snapshots__/extract.spec.ts.snap b/lib/manager/poetry/__snapshots__/extract.spec.ts.snap
index 3b0748c87d72004041a8ba16c5849d8bc9b3a1c5..507777ba84914ecbe411a13a4497f678e3e1b88a 100644
--- a/lib/manager/poetry/__snapshots__/extract.spec.ts.snap
+++ b/lib/manager/poetry/__snapshots__/extract.spec.ts.snap
@@ -548,3 +548,25 @@ Array [
   },
 ]
 `;
+
+exports[`lib/manager/poetry/extract extractPackageFile() resolves lockedVersions from the lockfile 1`] = `
+Object {
+  "constraints": Object {
+    "python": "^3.9",
+  },
+  "deps": Array [
+    Object {
+      "currentValue": "*",
+      "datasource": "pypi",
+      "depName": "boto3",
+      "depType": "dependencies",
+      "lockedVersion": "1.17.5",
+      "managerData": Object {
+        "nestedVersion": false,
+      },
+      "versioning": "poetry",
+    },
+  ],
+  "registryUrls": null,
+}
+`;
diff --git a/lib/manager/poetry/extract.spec.ts b/lib/manager/poetry/extract.spec.ts
index b91ca400507a3ffdb85000d2082cf21a4e9a2dc9..a66d7904eebf374092f0c490397ab4570bf49fac 100644
--- a/lib/manager/poetry/extract.spec.ts
+++ b/lib/manager/poetry/extract.spec.ts
@@ -1,6 +1,9 @@
 import { readFileSync } from 'fs';
+import { fs } from '../../../test/util';
 import { extractPackageFile } from './extract';
 
+jest.mock('../../util/fs');
+
 const pyproject1toml = readFileSync(
   'lib/manager/poetry/__fixtures__/pyproject.1.toml',
   'utf8'
@@ -46,6 +49,18 @@ const pyproject9toml = readFileSync(
   'utf8'
 );
 
+// pyproject.10.toml use by artifacts
+
+const pyproject11toml = readFileSync(
+  'lib/manager/poetry/__fixtures__/pyproject.11.toml',
+  'utf8'
+);
+
+const pyproject11tomlLock = readFileSync(
+  'lib/manager/poetry/__fixtures__/pyproject.11.toml.lock',
+  'utf8'
+);
+
 describe('lib/manager/poetry/extract', () => {
   describe('extractPackageFile()', () => {
     let filename: string;
@@ -58,14 +73,14 @@ describe('lib/manager/poetry/extract', () => {
     afterEach(() => {
       process.env = OLD_ENV;
     });
-    it('returns null for empty', () => {
-      expect(extractPackageFile('nothing here', filename)).toBeNull();
+    it('returns null for empty', async () => {
+      expect(await extractPackageFile('nothing here', filename)).toBeNull();
     });
-    it('returns null for parsed file without poetry section', () => {
-      expect(extractPackageFile(pyproject5toml, filename)).toBeNull();
+    it('returns null for parsed file without poetry section', async () => {
+      expect(await extractPackageFile(pyproject5toml, filename)).toBeNull();
     });
-    it('extracts multiple dependencies', () => {
-      const res = extractPackageFile(pyproject1toml, filename);
+    it('extracts multiple dependencies', async () => {
+      const res = await extractPackageFile(pyproject1toml, filename);
       expect(res.deps).toMatchSnapshot();
       expect(res.deps).toHaveLength(9);
       expect(res.constraints).toEqual({
@@ -73,72 +88,77 @@ describe('lib/manager/poetry/extract', () => {
         python: '~2.7 || ^3.4',
       });
     });
-    it('extracts multiple dependencies (with dep = {version = "1.2.3"} case)', () => {
-      const res = extractPackageFile(pyproject2toml, filename);
+    it('extracts multiple dependencies (with dep = {version = "1.2.3"} case)', async () => {
+      const res = await extractPackageFile(pyproject2toml, filename);
       expect(res.deps).toMatchSnapshot();
       expect(res.deps).toHaveLength(7);
     });
-    it('handles case with no dependencies', () => {
-      const res = extractPackageFile(pyproject3toml, filename);
+    it('handles case with no dependencies', async () => {
+      const res = await extractPackageFile(pyproject3toml, filename);
       expect(res).toBeNull();
     });
-    it('handles multiple constraint dependencies', () => {
-      const res = extractPackageFile(pyproject4toml, filename);
+    it('handles multiple constraint dependencies', async () => {
+      const res = await extractPackageFile(pyproject4toml, filename);
       expect(res.deps).toMatchSnapshot();
       expect(res.deps).toHaveLength(1);
     });
-    it('extracts registries', () => {
-      const res = extractPackageFile(pyproject6toml, filename);
+    it('extracts registries', async () => {
+      const res = await extractPackageFile(pyproject6toml, filename);
       expect(res.registryUrls).toMatchSnapshot();
       expect(res.registryUrls).toHaveLength(3);
     });
-    it('can parse empty registries', () => {
-      const res = extractPackageFile(pyproject7toml, filename);
+    it('can parse empty registries', async () => {
+      const res = await extractPackageFile(pyproject7toml, filename);
       expect(res.registryUrls).toBeNull();
     });
-    it('can parse missing registries', () => {
-      const res = extractPackageFile(pyproject1toml, filename);
+    it('can parse missing registries', async () => {
+      const res = await extractPackageFile(pyproject1toml, filename);
       expect(res.registryUrls).toBeNull();
     });
-    it('dedupes registries', () => {
-      const res = extractPackageFile(pyproject8toml, filename);
+    it('dedupes registries', async () => {
+      const res = await extractPackageFile(pyproject8toml, filename);
       expect(res.registryUrls).toMatchSnapshot();
     });
-    it('extracts mixed versioning types', () => {
-      const res = extractPackageFile(pyproject9toml, filename);
+    it('extracts mixed versioning types', async () => {
+      const res = await extractPackageFile(pyproject9toml, filename);
+      expect(res).toMatchSnapshot();
+    });
+    it('resolves lockedVersions from the lockfile', async () => {
+      fs.readLocalFile.mockResolvedValue(pyproject11tomlLock);
+      const res = await extractPackageFile(pyproject11toml, filename);
       expect(res).toMatchSnapshot();
     });
-    it('skips git dependencies', () => {
+    it('skips git dependencies', async () => {
       const content =
         '[tool.poetry.dependencies]\r\nflask = {git = "https://github.com/pallets/flask.git"}\r\nwerkzeug = ">=0.14"';
-      const res = extractPackageFile(content, filename).deps;
+      const res = (await extractPackageFile(content, filename)).deps;
       expect(res[0].depName).toBe('flask');
       expect(res[0].currentValue).toBe('');
       expect(res[0].skipReason).toBe('git-dependency');
       expect(res).toHaveLength(2);
     });
-    it('skips git dependencies with version', () => {
+    it('skips git dependencies with version', async () => {
       const content =
         '[tool.poetry.dependencies]\r\nflask = {git = "https://github.com/pallets/flask.git", version="1.2.3"}\r\nwerkzeug = ">=0.14"';
-      const res = extractPackageFile(content, filename).deps;
+      const res = (await extractPackageFile(content, filename)).deps;
       expect(res[0].depName).toBe('flask');
       expect(res[0].currentValue).toBe('1.2.3');
       expect(res[0].skipReason).toBe('git-dependency');
       expect(res).toHaveLength(2);
     });
-    it('skips path dependencies', () => {
+    it('skips path dependencies', async () => {
       const content =
         '[tool.poetry.dependencies]\r\nflask = {path = "/some/path/"}\r\nwerkzeug = ">=0.14"';
-      const res = extractPackageFile(content, filename).deps;
+      const res = (await extractPackageFile(content, filename)).deps;
       expect(res[0].depName).toBe('flask');
       expect(res[0].currentValue).toBe('');
       expect(res[0].skipReason).toBe('path-dependency');
       expect(res).toHaveLength(2);
     });
-    it('skips path dependencies with version', () => {
+    it('skips path dependencies with version', async () => {
       const content =
         '[tool.poetry.dependencies]\r\nflask = {path = "/some/path/", version = "1.2.3"}\r\nwerkzeug = ">=0.14"';
-      const res = extractPackageFile(content, filename).deps;
+      const res = (await extractPackageFile(content, filename)).deps;
       expect(res[0].depName).toBe('flask');
       expect(res[0].currentValue).toBe('1.2.3');
       expect(res[0].skipReason).toBe('path-dependency');
diff --git a/lib/manager/poetry/extract.ts b/lib/manager/poetry/extract.ts
index fb2bc1485326e38d3fbc6c5deab7876dfea69787..5b17c06e04e2673914bd4ea7fce1d05869fa5201 100644
--- a/lib/manager/poetry/extract.ts
+++ b/lib/manager/poetry/extract.ts
@@ -3,20 +3,28 @@ import is from '@sindresorhus/is';
 import * as datasourcePypi from '../../datasource/pypi';
 import { logger } from '../../logger';
 import { SkipReason } from '../../types';
+import { getSiblingFileName, readLocalFile } from '../../util/fs';
 import * as pep440Versioning from '../../versioning/pep440';
 import * as poetryVersioning from '../../versioning/poetry';
 import { PackageDependency, PackageFile } from '../common';
-import { PoetryFile, PoetrySection } from './types';
+import {
+  PoetryFile,
+  PoetryLock,
+  PoetryLockSection,
+  PoetrySection,
+} from './types';
 
 function extractFromSection(
   parsedFile: PoetryFile,
-  section: keyof PoetrySection
+  section: keyof PoetrySection,
+  poetryLockfile: Record<string, PoetryLockSection>
 ): PackageDependency[] {
   const deps = [];
   const sectionContent = parsedFile.tool.poetry[section];
   if (!sectionContent) {
     return [];
   }
+
   Object.keys(sectionContent).forEach((depName) => {
     if (depName === 'python') {
       return;
@@ -55,6 +63,9 @@ function extractFromSection(
       managerData: { nestedVersion },
       datasource: datasourcePypi.id,
     };
+    if (dep.depName in poetryLockfile) {
+      dep.lockedVersion = poetryLockfile[dep.depName].version;
+    }
     if (skipReason) {
       dep.skipReason = skipReason;
     } else if (pep440Versioning.isValid(dep.currentValue)) {
@@ -87,10 +98,10 @@ function extractRegistries(pyprojectfile: PoetryFile): string[] {
   return Array.from(registryUrls);
 }
 
-export function extractPackageFile(
+export async function extractPackageFile(
   content: string,
   fileName: string
-): PackageFile | null {
+): Promise<PackageFile | null> {
   logger.trace(`poetry.extractPackageFile(${fileName})`);
   let pyprojectfile: PoetryFile;
   try {
@@ -103,10 +114,30 @@ export function extractPackageFile(
     logger.debug(`${fileName} contains no poetry section`);
     return null;
   }
+
+  // handle the lockfile
+  const lockfileName = getSiblingFileName(fileName, 'poetry.lock');
+  const lockContents = await readLocalFile(lockfileName, 'utf8');
+
+  let poetryLockfile: PoetryLock;
+  try {
+    poetryLockfile = parse(lockContents);
+  } catch (err) {
+    logger.debug({ err }, 'Error parsing pyproject.toml file');
+  }
+
+  const lockfileMapping: Record<string, PoetryLockSection> = {};
+  if (poetryLockfile?.package) {
+    // Create a package->PoetryLockSection mapping
+    for (const poetryPackage of poetryLockfile.package) {
+      lockfileMapping[poetryPackage.name] = poetryPackage;
+    }
+  }
+
   const deps = [
-    ...extractFromSection(pyprojectfile, 'dependencies'),
-    ...extractFromSection(pyprojectfile, 'dev-dependencies'),
-    ...extractFromSection(pyprojectfile, 'extras'),
+    ...extractFromSection(pyprojectfile, 'dependencies', lockfileMapping),
+    ...extractFromSection(pyprojectfile, 'dev-dependencies', lockfileMapping),
+    ...extractFromSection(pyprojectfile, 'extras', lockfileMapping),
   ];
   if (!deps.length) {
     return null;
diff --git a/lib/manager/poetry/types.ts b/lib/manager/poetry/types.ts
index 43330ece85b05c7677158e63396d3cb098a99734..5f4803e48a248dd9e72cf14c9858adb6a079668e 100644
--- a/lib/manager/poetry/types.ts
+++ b/lib/manager/poetry/types.ts
@@ -26,3 +26,12 @@ export interface PoetrySource {
   name?: string;
   url?: string;
 }
+
+export interface PoetryLockSection {
+  name?: string;
+  version?: string;
+}
+
+export interface PoetryLock {
+  package?: PoetryLockSection[];
+}
diff --git a/lib/versioning/poetry/index.spec.ts b/lib/versioning/poetry/index.spec.ts
index a6175e7abcc2223281dbb73cd7e3fb63054f2d3e..d9f882c53d5b03fb74a37ab8b8cd5960ec87a04b 100644
--- a/lib/versioning/poetry/index.spec.ts
+++ b/lib/versioning/poetry/index.spec.ts
@@ -13,6 +13,9 @@ describe('semver.isValid(input)', () => {
   it('should reject semver without dash', () => {
     expect(semver.isValid('1.2.3foo')).toBeFalsy();
   });
+  it('should work with wildcards', () => {
+    expect(semver.isValid('*')).toBeTruthy();
+  });
   it('should support ranges', () => {
     expect(semver.isValid('~1.2.3')).toBeTruthy();
     expect(semver.isValid('^1.2.3')).toBeTruthy();
@@ -48,6 +51,9 @@ describe('semver.matches()', () => {
     expect(semver.matches('4.2.0', '4.3.0, 3.0.0')).toBe(false);
     expect(semver.matches('4.2.0', '> 5.0.0, <= 6.0.0')).toBe(false);
   });
+  it('handles wildcards', () => {
+    expect(semver.matches('4.2.0', '*')).toBe(true);
+  });
 });
 describe('semver.isLessThanRange()', () => {
   it('handles comma', () => {