diff --git a/bin/system-info.c b/bin/system-info.c
index 56b1cb911dd81ab5621962f01581d21fa203b054..7a11d2267958a75a7fce086bfb0d78d31aefd399 100644
--- a/bin/system-info.c
+++ b/bin/system-info.c
@@ -632,6 +632,46 @@ jsonify_os_release (JsonBuilder *builder,
   json_builder_end_object (builder);
 }
 
+static void
+jsonify_container (JsonBuilder *builder,
+                   SrtSystemInfo *info)
+{
+  SrtContainerType type = srt_system_info_get_container_type (info);
+  gchar *host_directory = srt_system_info_dup_container_host_directory (info);
+  gchar **env = NULL;
+  SrtSystemInfo *host = NULL;
+
+  json_builder_set_member_name (builder, "container");
+  json_builder_begin_object (builder);
+    {
+      json_builder_set_member_name (builder, "type");
+      jsonify_enum (builder, SRT_TYPE_CONTAINER_TYPE, type);
+
+      if (type != SRT_CONTAINER_TYPE_NONE)
+        {
+          json_builder_set_member_name (builder, "host");
+          json_builder_begin_object (builder);
+            {
+              json_builder_set_member_name (builder, "path");
+              json_builder_add_string_value (builder, host_directory);
+
+              if (host_directory != NULL)
+                {
+                  host = srt_system_info_new (NULL);
+                  srt_system_info_set_sysroot (host, host_directory);
+                  jsonify_os_release (builder, host);
+                }
+            }
+          json_builder_end_object (builder);
+        }
+    }
+  json_builder_end_object (builder);
+
+  g_free (host_directory);
+  g_clear_object (&host);
+  g_strfreev (env);
+}
+
 static const char * const locales[] =
 {
   "",
@@ -847,6 +887,7 @@ main (int argc,
   json_builder_end_object (builder);
 
   jsonify_os_release (builder, info);
+  jsonify_container (builder, info);
 
   json_builder_set_member_name (builder, "driver_environment");
   json_builder_begin_array (builder);
diff --git a/bin/system-info.md b/bin/system-info.md
index e3fe6a543443246c91ec3ea8867b76deeb6c783e..6c342e024727f92aef4b80dd8fed38e7ef0a8147 100644
--- a/bin/system-info.md
+++ b/bin/system-info.md
@@ -292,6 +292,29 @@ keys:
 
         This is the **VARIANT** from **os-release**(5).
 
+**container**
+:   Details of the container we are running in, if any.
+    The keys are strings:
+
+    **type**
+    :   A short machine-readable string identifying the container type,
+        such as **flatpak**, **pressure-vessel**, **docker**,
+        **unknown** (if we appear to be in a container but the type is
+        unknown), or **none** (if we do not appear to be in a container).
+
+    **host**
+    :   An object describing the host system. The keys are strings:
+
+        **path**
+        :   Absolute path to a directory where important files from the
+            host system can be found, or **null** if unavailable.
+            In Flatpak and pressure-vessel, this will be either **/run/host**
+            or **null*.
+
+        **os-release**
+        :   The same as the **os-release** described above, but describing
+            the host system rather than the container.
+
 **driver_environment**
 :   An array of strings, in the form "NAME=VALUE", with the well-known driver environment
     variables that are currently set.
diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c
index 63dc2c409dc9c7c1d47e0b388f3b1d82eea0b8d9..8693849b8c74be67c22640f3fda23f4675911125 100644
--- a/steam-runtime-tools/system-info.c
+++ b/steam-runtime-tools/system-info.c
@@ -150,6 +150,12 @@ struct _SrtSystemInfo
     gchar **messages_64;
     gboolean have_data;
   } pinned_libs;
+  struct
+  {
+    SrtContainerType type;
+    gchar *host_directory;
+    gboolean have_data;
+  } container;
   SrtOsRelease os_release;
   SrtTestFlags test_flags;
   Tristate can_write_uinput;
@@ -300,6 +306,8 @@ srt_system_info_init (SrtSystemInfo *self)
   self->abis = g_ptr_array_new_full (2, abi_free);
 
   _srt_os_release_init (&self->os_release);
+
+  self->container.type = SRT_CONTAINER_TYPE_UNKNOWN;
 }
 
 static void
@@ -342,6 +350,17 @@ srt_system_info_set_property (GObject *object,
     }
 }
 
+/*
+ * Forget any cached information about container information.
+ */
+static void
+forget_container_info (SrtSystemInfo *self)
+{
+  g_clear_pointer (&self->container.host_directory, g_free);
+  self->container.type = SRT_CONTAINER_TYPE_UNKNOWN;
+  self->container.have_data = FALSE;
+}
+
 /*
  * Forget any cached information about locales.
  */
@@ -431,6 +450,7 @@ srt_system_info_finalize (GObject *object)
 {
   SrtSystemInfo *self = SRT_SYSTEM_INFO (object);
 
+  forget_container_info (self);
   forget_icds (self);
   forget_locales (self);
   forget_os (self);
@@ -1592,6 +1612,7 @@ srt_system_info_set_sysroot (SrtSystemInfo *self,
 {
   g_return_if_fail (SRT_IS_SYSTEM_INFO (self));
 
+  forget_container_info (self);
   forget_graphics_modules (self);
   forget_libraries (self);
   forget_graphics_results (self);
@@ -2742,3 +2763,188 @@ srt_system_info_list_driver_environment (SrtSystemInfo *self)
   else
     return g_strdupv (self->cached_driver_environment);
 }
+
+typedef struct
+{
+  SrtContainerType type;
+  const char *name;
+} ContainerTypeName;
+
+static const ContainerTypeName container_types[] =
+{
+  { SRT_CONTAINER_TYPE_DOCKER, "docker" }
+};
+
+static SrtContainerType
+container_type_from_name (const char *name)
+{
+  gsize i;
+
+  for (i = 0; i < G_N_ELEMENTS (container_types); i++)
+    {
+      const ContainerTypeName *entry = &container_types[i];
+
+      if (strcmp (entry->name, name) == 0)
+        return entry->type;
+    }
+
+  return SRT_CONTAINER_TYPE_UNKNOWN;
+}
+
+static void
+ensure_container_info (SrtSystemInfo *self)
+{
+  const char *sysroot = NULL;
+  gchar *contents = NULL;
+  gchar *filename = NULL;
+
+  if (self->container.have_data)
+    return;
+
+  g_assert (self->container.host_directory == NULL);
+  g_assert (self->container.type == SRT_CONTAINER_TYPE_UNKNOWN);
+
+  sysroot = self->sysroot;
+
+  if (sysroot == NULL)
+    sysroot = "/";
+
+  g_debug ("Finding container info in sysroot %s...", sysroot);
+
+  filename = g_build_filename (sysroot, "run", "systemd", "container", NULL);
+
+  if (g_file_get_contents (filename, &contents, NULL, NULL))
+    {
+      g_strchomp (contents);
+      self->container.type = container_type_from_name (contents);
+      g_debug ("Type %d based on %s", self->container.type, filename);
+      goto out;
+    }
+
+  g_clear_pointer (&filename, g_free);
+  filename = g_build_filename (sysroot, ".flatpak-info", NULL);
+
+  if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+    {
+      self->container.type = SRT_CONTAINER_TYPE_FLATPAK;
+      g_debug ("Flatpak based on %s", filename);
+      goto out;
+    }
+
+  g_clear_pointer (&filename, g_free);
+  filename = g_build_filename (sysroot, "run", "pressure-vessel", NULL);
+
+  if (g_file_test (filename, G_FILE_TEST_IS_DIR))
+    {
+      self->container.type = SRT_CONTAINER_TYPE_PRESSURE_VESSEL;
+      g_debug ("pressure-vessel based on %s", filename);
+      goto out;
+    }
+
+  g_clear_pointer (&filename, g_free);
+  filename = g_build_filename (sysroot, ".dockerenv", NULL);
+
+  if (g_file_test (filename, G_FILE_TEST_EXISTS))
+    {
+      self->container.type = SRT_CONTAINER_TYPE_DOCKER;
+      g_debug ("Docker based on %s", filename);
+      goto out;
+    }
+
+  g_clear_pointer (&filename, g_free);
+  filename = g_build_filename (sysroot, "proc", "1", "cgroup", NULL);
+
+  if (g_file_get_contents (filename, &contents, NULL, NULL))
+    {
+      if (strstr (contents, "/docker/") != NULL)
+        self->container.type = SRT_CONTAINER_TYPE_DOCKER;
+
+      if (self->container.type != SRT_CONTAINER_TYPE_UNKNOWN)
+        {
+          g_debug ("Type %d based on %s", self->container.type, filename);
+          goto out;
+        }
+    }
+
+  g_clear_pointer (&filename, g_free);
+  filename = g_build_filename (sysroot, "run", "host", NULL);
+
+  if (g_file_test (filename, G_FILE_TEST_IS_DIR))
+    {
+      g_debug ("Unknown container technology based on %s", filename);
+      self->container.type = SRT_CONTAINER_TYPE_UNKNOWN;
+      self->container.host_directory = g_steal_pointer (&filename);
+      goto out;
+    }
+
+  g_clear_pointer (&filename, g_free);
+
+  /* We haven't found any particular evidence of being in a container */
+  g_debug ("Probably not a container");
+  self->container.type = SRT_CONTAINER_TYPE_NONE;
+
+out:
+  g_free (contents);
+  g_free (filename);
+
+  switch (self->container.type)
+    {
+      case SRT_CONTAINER_TYPE_FLATPAK:
+      case SRT_CONTAINER_TYPE_PRESSURE_VESSEL:
+        self->container.host_directory = g_build_filename (sysroot, "run",
+                                                           "host", NULL);
+        break;
+
+      case SRT_CONTAINER_TYPE_DOCKER:
+      case SRT_CONTAINER_TYPE_UNKNOWN:
+      case SRT_CONTAINER_TYPE_NONE:
+      default:
+        break;
+    }
+
+  self->container.have_data = TRUE;
+}
+
+/**
+ * srt_system_info_get_container_type:
+ * @self: The #SrtSystemInfo object
+ *
+ * If the program appears to be running in a container, return what sort
+ * of container it is.
+ *
+ * Returns: A recognised container type, or %SRT_CONTAINER_TYPE_NONE
+ *  if a container cannot be detected, or %SRT_CONTAINER_TYPE_UNKNOWN
+ *  if unsure.
+ */
+SrtContainerType
+srt_system_info_get_container_type (SrtSystemInfo *self)
+{
+  g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), SRT_CONTAINER_TYPE_UNKNOWN);
+
+  ensure_container_info (self);
+  return self->container.type;
+}
+
+/**
+ * srt_system_info_dup_container_host_directory:
+ *
+ * If the program appears to be running in a container, return the
+ * directory where host files can be found. For example, if this function
+ * returns `/run/host`, it might be possible to load the host system's
+ * `/usr/lib/os-release` by reading `/run/host/usr/lib/os-release`.
+ *
+ * The returned directory is usually not complete. For example,
+ * in a Flatpak app, `/run/host` will sometimes contain the host system's
+ * `/etc` and `/usr`, but only if suitable permissions flags are set.
+ *
+ * Returns: A path from which at least some host-system files can be
+ *  loaded, typically `/run/host`, or %NULL if unknown or unavailable
+ */
+gchar *
+srt_system_info_dup_container_host_directory (SrtSystemInfo *self)
+{
+  g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL);
+
+  ensure_container_info (self);
+  return g_strdup (self->container.host_directory);
+}
diff --git a/steam-runtime-tools/system-info.h b/steam-runtime-tools/system-info.h
index fe3c7da685c08fd665a6caa66763e6e59311e123..da5e80e43257f0eb9fc2316463c0c59f3e6eea7b 100644
--- a/steam-runtime-tools/system-info.h
+++ b/steam-runtime-tools/system-info.h
@@ -71,6 +71,26 @@ typedef enum
   SRT_DRIVER_FLAGS_NONE = 0
 } SrtDriverFlags;
 
+/**
+ * SrtContainerType:
+ * @SRT_CONTAINER_TYPE_UNKNOWN: Unknown container type
+ * @SRT_CONTAINER_TYPE_NONE: No container detected
+ * @SRT_CONTAINER_TYPE_FLATPAK: Running in a Flatpak app
+ * @SRT_CONTAINER_TYPE_PRESSURE_VESSEL: Running in a Steam Runtime container
+ *  using pressure-vessel
+ * @SRT_CONTAINER_TYPE_DOCKER: Running in a Docker container
+ *
+ * A type of container.
+ */
+typedef enum
+{
+  SRT_CONTAINER_TYPE_NONE = 0,
+  SRT_CONTAINER_TYPE_FLATPAK,
+  SRT_CONTAINER_TYPE_PRESSURE_VESSEL,
+  SRT_CONTAINER_TYPE_DOCKER,
+  SRT_CONTAINER_TYPE_UNKNOWN = -1
+} SrtContainerType;
+
 typedef struct _SrtSystemInfo SrtSystemInfo;
 typedef struct _SrtSystemInfoClass SrtSystemInfoClass;
 
@@ -87,6 +107,9 @@ SrtSystemInfo *srt_system_info_new (const char *expectations);
 
 gboolean srt_system_info_can_run (SrtSystemInfo *self,
                                   const char *multiarch_tuple);
+SrtContainerType srt_system_info_get_container_type (SrtSystemInfo *self);
+gchar *srt_system_info_dup_container_host_directory (SrtSystemInfo *self);
+
 gboolean srt_system_info_can_write_to_uinput (SrtSystemInfo *self);
 SrtLibraryIssues srt_system_info_check_libraries (SrtSystemInfo *self,
                                                   const gchar *multiarch_tuple,
diff --git a/tests/sysroots/debian-unstable/.dockerenv b/tests/sysroots/debian-unstable/.dockerenv
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/sysroots/debian10/run/systemd/container b/tests/sysroots/debian10/run/systemd/container
new file mode 100644
index 0000000000000000000000000000000000000000..982793c32ee7f2df6107a721a7814fcf5ec39bbd
--- /dev/null
+++ b/tests/sysroots/debian10/run/systemd/container
@@ -0,0 +1 @@
+whatever
diff --git a/tests/sysroots/fedora/run/systemd/container b/tests/sysroots/fedora/run/systemd/container
new file mode 100644
index 0000000000000000000000000000000000000000..bdb9670965e4db736d9bdab6edce7cf0d688bea9
--- /dev/null
+++ b/tests/sysroots/fedora/run/systemd/container
@@ -0,0 +1 @@
+docker
diff --git a/tests/sysroots/flatpak-example/run/host/.exists b/tests/sysroots/flatpak-example/run/host/.exists
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/sysroots/invalid-os-release/run/host/.exists b/tests/sysroots/invalid-os-release/run/host/.exists
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/sysroots/steamrt-unofficial/proc/1/cgroup b/tests/sysroots/steamrt-unofficial/proc/1/cgroup
new file mode 100644
index 0000000000000000000000000000000000000000..72dac858cf44f4c3a0b6e73276a9e6d8e770e767
--- /dev/null
+++ b/tests/sysroots/steamrt-unofficial/proc/1/cgroup
@@ -0,0 +1,12 @@
+11:perf_event:/docker/9999999999999999999999999999999999999999999999999999999999999999
+10:freezer:/docker/9999999999999999999999999999999999999999999999999999999999999999
+9:memory:/docker/9999999999999999999999999999999999999999999999999999999999999999
+8:rdma:/
+7:devices:/docker/9999999999999999999999999999999999999999999999999999999999999999
+6:blkio:/docker/9999999999999999999999999999999999999999999999999999999999999999
+5:net_cls,net_prio:/docker/9999999999999999999999999999999999999999999999999999999999999999
+4:cpu,cpuacct:/docker/9999999999999999999999999999999999999999999999999999999999999999
+3:cpuset:/docker/9999999999999999999999999999999999999999999999999999999999999999
+2:pids:/docker/9999999999999999999999999999999999999999999999999999999999999999
+1:name=systemd:/docker/9999999999999999999999999999999999999999999999999999999999999999
+0::/system.slice/docker.service
diff --git a/tests/sysroots/steamrt/run/pressure-vessel/.exists b/tests/sysroots/steamrt/run/pressure-vessel/.exists
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/system-info.c b/tests/system-info.c
index cb1d11930130200dc41f634d55fe01c8c2a9f749..76f679ac4a4316b2c358acc846e7553f4afdcdfa 100644
--- a/tests/system-info.c
+++ b/tests/system-info.c
@@ -2182,6 +2182,75 @@ driver_environment (Fixture *f,
   g_strfreev (envp);
 }
 
+typedef struct
+{
+  const char *description;
+  const char *sysroot;
+  SrtContainerType type;
+  const char *host_directory;
+} ContainerTest;
+
+static const ContainerTest container_tests[] =
+{
+  { "Has /.dockerenv",
+    "debian-unstable", SRT_CONTAINER_TYPE_DOCKER, NULL },
+  { "Has an unknown value in /run/systemd/container",
+    "debian10", SRT_CONTAINER_TYPE_UNKNOWN, NULL },
+  { "Has 'docker' in /run/systemd/container",
+    "fedora", SRT_CONTAINER_TYPE_DOCKER, NULL },
+  { "Has /.flatpak-info",
+    "flatpak-example", SRT_CONTAINER_TYPE_FLATPAK, "/run/host" },
+  { "Has /run/host",
+    "invalid-os-release", SRT_CONTAINER_TYPE_UNKNOWN, "/run/host" },
+  { "Has no evidence of being a container",
+    "no-os-release", SRT_CONTAINER_TYPE_NONE, NULL },
+  { "Has /run/pressure-vessel",
+    "steamrt", SRT_CONTAINER_TYPE_PRESSURE_VESSEL, "/run/host" },
+  { "Has a Docker-looking /proc/1/cgroup",
+    "steamrt-unofficial", SRT_CONTAINER_TYPE_DOCKER, NULL },
+};
+
+static void
+test_containers (Fixture *f,
+                 gconstpointer context)
+{
+  gsize i, j;
+
+  for (i = 0; i < G_N_ELEMENTS (container_tests); i++)
+    {
+      const ContainerTest *test = &container_tests[i];
+      SrtSystemInfo *info;
+      gchar *sysroot, *got, *expected;
+
+      g_test_message ("%s: %s", test->sysroot, test->description);
+
+      sysroot = g_build_filename (f->srcdir, "sysroots", test->sysroot, NULL);
+
+      info = srt_system_info_new (NULL);
+      g_assert_nonnull (info);
+      srt_system_info_set_sysroot (info, sysroot);
+
+      for (j = 0; j < 2; j++)
+        {
+          g_assert_cmpint (srt_system_info_get_container_type (info), ==,
+                           test->type);
+          got = srt_system_info_dup_container_host_directory (info);
+
+          if (test->host_directory == NULL)
+            expected = NULL;
+          else
+            expected = g_build_filename (sysroot, test->host_directory, NULL);
+
+          g_assert_cmpstr (got, ==, expected);
+          g_free (got);
+          g_free (expected);
+        }
+
+      g_object_unref (info);
+      g_free (sysroot);
+    }
+}
+
 int
 main (int argc,
       char **argv)
@@ -2252,5 +2321,8 @@ main (int argc,
   g_test_add ("/system-info/driver_environment", Fixture, NULL,
               setup, driver_environment, teardown);
 
+  g_test_add ("/system-info/containers", Fixture, NULL,
+              setup, test_containers, teardown);
+
   return g_test_run ();
 }