diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go
index 234d683bbf..86bc8b9731 100644
--- a/cmd/podman/checkpoint.go
+++ b/cmd/podman/checkpoint.go
@@ -46,6 +46,7 @@ func init() {
 	flags.BoolVar(&checkpointCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections")
 	flags.BoolVarP(&checkpointCommand.All, "all", "a", false, "Checkpoint all running containers")
 	flags.BoolVarP(&checkpointCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
+	flags.StringVarP(&checkpointCommand.Export, "export", "e", "", "Export the checkpoint image to a tar.gz")
 	markFlagHiddenForRemoteClient("latest", flags)
 }
 
@@ -64,6 +65,7 @@ func checkpointCmd(c *cliconfig.CheckpointValues) error {
 		Keep:           c.Keep,
 		KeepRunning:    c.LeaveRunning,
 		TCPEstablished: c.TcpEstablished,
+		TargetFile:     c.Export,
 	}
 	return runtime.Checkpoint(c, options)
 }
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 61ea26cf71..b8b1648b87 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -91,6 +91,7 @@ type CheckpointValues struct {
 	TcpEstablished bool
 	All            bool
 	Latest         bool
+	Export         string
 }
 
 type CommitValues struct {
@@ -428,6 +429,8 @@ type RestoreValues struct {
 	Keep           bool
 	Latest         bool
 	TcpEstablished bool
+	Import         string
+	Name           string
 }
 
 type RmValues struct {
diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go
index 8cfd5ca0df..9c77d4a5e3 100644
--- a/cmd/podman/restore.go
+++ b/cmd/podman/restore.go
@@ -24,10 +24,10 @@ var (
 			restoreCommand.InputArgs = args
 			restoreCommand.GlobalFlags = MainGlobalOpts
 			restoreCommand.Remote = remoteclient
-			return restoreCmd(&restoreCommand)
+			return restoreCmd(&restoreCommand, cmd)
 		},
 		Args: func(cmd *cobra.Command, args []string) error {
-			return checkAllAndLatest(cmd, args, false)
+			return checkAllAndLatest(cmd, args, true)
 		},
 		Example: `podman container restore ctrID
   podman container restore --latest
@@ -43,13 +43,14 @@ func init() {
 	flags.BoolVarP(&restoreCommand.All, "all", "a", false, "Restore all checkpointed containers")
 	flags.BoolVarP(&restoreCommand.Keep, "keep", "k", false, "Keep all temporary checkpoint files")
 	flags.BoolVarP(&restoreCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
-	// TODO: add ContainerStateCheckpointed
-	flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Checkpoint a container with established TCP connections")
+	flags.BoolVar(&restoreCommand.TcpEstablished, "tcp-established", false, "Restore a container with established TCP connections")
+	flags.StringVarP(&restoreCommand.Import, "import", "i", "", "Restore from exported checkpoint archive (tar.gz)")
+	flags.StringVarP(&restoreCommand.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)")
 
 	markFlagHiddenForRemoteClient("latest", flags)
 }
 
-func restoreCmd(c *cliconfig.RestoreValues) error {
+func restoreCmd(c *cliconfig.RestoreValues, cmd *cobra.Command) error {
 	if rootless.IsRootless() {
 		return errors.New("restoring a container requires root")
 	}
@@ -63,6 +64,20 @@ func restoreCmd(c *cliconfig.RestoreValues) error {
 	options := libpod.ContainerCheckpointOptions{
 		Keep:           c.Keep,
 		TCPEstablished: c.TcpEstablished,
+		TargetFile:     c.Import,
+		Name:           c.Name,
 	}
-	return runtime.Restore(c, options)
+
+	if c.Import == "" && c.Name != "" {
+		return errors.Errorf("--name can only used with --import")
+	}
+
+	if c.Name != "" && c.TcpEstablished {
+		return errors.Errorf("--tcp-established cannot be used with --name")
+	}
+
+	if (c.Import != "") && (c.All || c.Latest) {
+		return errors.Errorf("Cannot use --import and --all or --latest at the same time")
+	}
+	return runtime.Restore(getContext(), c, options)
 }
diff --git a/completions/bash/podman b/completions/bash/podman
index 49c8c0e52c..efb8a6a9b9 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -742,6 +742,10 @@ _podman_container_attach() {
 }
 
 _podman_container_checkpoint() {
+     local options_with_args="
+     -e
+     --export
+     "
      local boolean_options="
      -a
      --all
@@ -755,9 +759,15 @@ _podman_container_checkpoint() {
      --leave-running
      --tcp-established
      "
+     case "$prev" in
+        -e|--export)
+            _filedir
+            return
+            ;;
+     esac
      case "$cur" in
 	-*)
-	    COMPREPLY=($(compgen -W "$boolean_options" -- "$cur"))
+	    COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
 	    ;;
 	*)
 	    __podman_complete_containers_running
@@ -844,6 +854,12 @@ _podman_container_restart() {
 }
 
 _podman_container_restore() {
+     local options_with_args="
+     -i
+     --import
+     -n
+     --name
+     "
      local boolean_options="
 	  -a
 	  --all
@@ -855,9 +871,15 @@ _podman_container_restore() {
 	  --latest
 	  --tcp-established
      "
+     case "$prev" in
+        -i|--import)
+            _filedir
+            return
+            ;;
+     esac
      case "$cur" in
 	-*)
-	    COMPREPLY=($(compgen -W "$boolean_options" -- "$cur"))
+	    COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
 	    ;;
 	*)
 	    __podman_complete_containers_created
diff --git a/docs/podman-container-checkpoint.1.md b/docs/podman-container-checkpoint.1.md
index 79dc12261b..afccdf59ac 100644
--- a/docs/podman-container-checkpoint.1.md
+++ b/docs/podman-container-checkpoint.1.md
@@ -38,6 +38,12 @@ image contains established TCP connections, this options is required during
 restore. Defaults to not checkpointing containers with established TCP
 connections.
 
+**--export, -e**
+
+Export the checkpoint to a tar.gz file. The exported checkpoint can be used
+to import the container on another system and thus enabling container live
+migration.
+
 ## EXAMPLE
 
 podman container checkpoint mywebserver
diff --git a/docs/podman-container-restore.1.md b/docs/podman-container-restore.1.md
index e41f7c1d87..5efc280fee 100644
--- a/docs/podman-container-restore.1.md
+++ b/docs/podman-container-restore.1.md
@@ -42,6 +42,24 @@ If the checkpoint image does not contain established TCP connections this
 option is ignored. Defaults to not restoring containers with established TCP
 connections.
 
+**--import, -i**
+
+Import a checkpoint tar.gz file, which was exported by Podman. This can be used
+to import a checkpointed container from another host. It is not necessary to specify
+a container when restoring from an exported checkpoint.
+
+**--name, -n**
+
+This is only available in combination with **--import, -i**. If a container is restored
+from a checkpoint tar.gz file it is possible to rename it with **--name, -n**. This
+way it is possible to restore a container from a checkpoint multiple times with different
+names.
+
+If the **--name, -n** option is used, Podman will not attempt to assign the same IP
+address to the container it was using before checkpointing as each IP address can only
+be used once and the restored container will have another IP address. This also means
+that **--name, -n** cannot be used in combination with **--tcp-established**.
+
 ## EXAMPLE
 
 podman container restore mywebserver
diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md
index 032b7c8510..8b29264dbb 100644
--- a/docs/tutorials/podman_tutorial.md
+++ b/docs/tutorials/podman_tutorial.md
@@ -96,6 +96,28 @@ After being restored, the container will answer requests again as it did before
 curl http://<IP_address>:8080
 ```
 
+### Migrate the container
+To live migrate a container from one host to another the container is checkpointed on the source
+system of the migration, transferred to the destination system and then restored on the destination
+system. When transferring the checkpoint, it is possible to specify an output-file.
+
+On the source system:
+```console
+sudo podman container checkpoint <container_id> -e /tmp/checkpoint.tar.gz
+scp /tmp/checkpoint.tar.gz <destination_system>:/tmp
+```
+
+On the destination system:
+```console
+sudo podman container restore -i /tmp/checkpoint.tar.gz
+```
+
+After being restored, the container will answer requests again as it did before checkpointing. This
+time the container will continue to run on the destination system.
+```console
+curl http://<IP_address>:8080
+```
+
 ### Stopping the container
 To stop the httpd container:
 ```console
diff --git a/libpod/container_api.go b/libpod/container_api.go
index eff5bfe5f8..c27cb85ea9 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -815,11 +815,27 @@ type ContainerCheckpointOptions struct {
 	// TCPEstablished tells the API to checkpoint a container
 	// even if it contains established TCP connections
 	TCPEstablished bool
+	// Export tells the API to write the checkpoint image to
+	// the filename set in TargetFile
+	// Import tells the API to read the checkpoint image from
+	// the filename set in TargetFile
+	TargetFile string
+	// Name tells the API that during restore from an exported
+	// checkpoint archive a new name should be used for the
+	// restored container
+	Name string
 }
 
 // Checkpoint checkpoints a container
 func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointOptions) error {
 	logrus.Debugf("Trying to checkpoint container %s", c.ID())
+
+	if options.TargetFile != "" {
+		if err := c.prepareCheckpointExport(); err != nil {
+			return err
+		}
+	}
+
 	if !c.batched {
 		c.lock.Lock()
 		defer c.lock.Unlock()
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 5f8dd1c729..c0b5e4302a 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -21,6 +21,7 @@ import (
 	"github.com/containers/storage/pkg/archive"
 	"github.com/containers/storage/pkg/mount"
 	spec "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/opencontainers/runtime-tools/generate"
 	"github.com/opencontainers/selinux/go-selinux/label"
 	opentracing "github.com/opentracing/opentracing-go"
 	"github.com/pkg/errors"
@@ -1345,7 +1346,7 @@ func (c *Container) appendStringToRundir(destFile, output string) (string, error
 	return filepath.Join(c.state.RunDir, destFile), nil
 }
 
-// Save OCI spec to disk, replacing any existing specs for the container
+// saveSpec saves the OCI spec to disk, replacing any existing specs for the container
 func (c *Container) saveSpec(spec *spec.Spec) error {
 	// If the OCI spec already exists, we need to replace it
 	// Cannot guarantee some things, e.g. network namespaces, have the same
@@ -1501,3 +1502,40 @@ func (c *Container) checkReadyForRemoval() error {
 
 	return nil
 }
+
+// writeJSONFile marshalls and writes the given data to a JSON file
+// in the bundle path
+func (c *Container) writeJSONFile(v interface{}, file string) (err error) {
+	fileJSON, err := json.MarshalIndent(v, "", "  ")
+	if err != nil {
+		return errors.Wrapf(err, "error writing JSON to %s for container %s", file, c.ID())
+	}
+	file = filepath.Join(c.bundlePath(), file)
+	if err := ioutil.WriteFile(file, fileJSON, 0644); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// prepareCheckpointExport writes the config and spec to
+// JSON files for later export
+func (c *Container) prepareCheckpointExport() (err error) {
+	// save live config
+	if err := c.writeJSONFile(c.Config(), "config.dump"); err != nil {
+		return err
+	}
+
+	// save spec
+	jsonPath := filepath.Join(c.bundlePath(), "config.json")
+	g, err := generate.NewFromFile(jsonPath)
+	if err != nil {
+		logrus.Debugf("generating spec for container %q failed with %v", c.ID(), err)
+		return err
+	}
+	if err := c.writeJSONFile(g.Spec(), "spec.dump"); err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index f25f760929..4acc77afa8 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -5,6 +5,7 @@ package libpod
 import (
 	"context"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"net"
 	"os"
@@ -25,6 +26,7 @@ import (
 	"github.com/containers/libpod/pkg/lookup"
 	"github.com/containers/libpod/pkg/resolvconf"
 	"github.com/containers/libpod/pkg/rootless"
+	"github.com/containers/storage/pkg/archive"
 	securejoin "github.com/cyphar/filepath-securejoin"
 	"github.com/opencontainers/runc/libcontainer/user"
 	spec "github.com/opencontainers/runtime-spec/specs-go"
@@ -496,6 +498,45 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr
 	return nil
 }
 
+func (c *Container) exportCheckpoint(dest string) (err error) {
+	if (len(c.config.NamedVolumes) > 0) || (len(c.Dependencies()) > 0) {
+		return errors.Errorf("Cannot export checkpoints of containers with named volumes or dependencies")
+	}
+	logrus.Debugf("Exporting checkpoint image of container %q to %q", c.ID(), dest)
+	input, err := archive.TarWithOptions(c.bundlePath(), &archive.TarOptions{
+		Compression:      archive.Gzip,
+		IncludeSourceDir: true,
+		IncludeFiles: []string{
+			"checkpoint",
+			"artifacts",
+			"ctr.log",
+			"config.dump",
+			"spec.dump",
+			"network.status"},
+	})
+
+	if err != nil {
+		return errors.Wrapf(err, "error reading checkpoint directory %q", c.ID())
+	}
+
+	outFile, err := os.Create(dest)
+	if err != nil {
+		return errors.Wrapf(err, "error creating checkpoint export file %q", dest)
+	}
+	defer outFile.Close()
+
+	if err := os.Chmod(dest, 0600); err != nil {
+		return errors.Wrapf(err, "cannot chmod %q", dest)
+	}
+
+	_, err = io.Copy(outFile, input)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func (c *Container) checkpointRestoreSupported() (err error) {
 	if !criu.CheckForCriu() {
 		return errors.Errorf("Checkpoint/Restore requires at least CRIU %d", criu.MinCriuVersion)
@@ -549,6 +590,12 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
 		return err
 	}
 
+	if options.TargetFile != "" {
+		if err = c.exportCheckpoint(options.TargetFile); err != nil {
+			return err
+		}
+	}
+
 	logrus.Debugf("Checkpointed container %s", c.ID())
 
 	if !options.KeepRunning {
@@ -561,15 +608,50 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
 	}
 
 	if !options.Keep {
-		// Remove log file
-		os.Remove(filepath.Join(c.bundlePath(), "dump.log"))
-		// Remove statistic file
-		os.Remove(filepath.Join(c.bundlePath(), "stats-dump"))
+		cleanup := []string{
+			"dump.log",
+			"stats-dump",
+			"config.dump",
+			"spec.dump",
+		}
+		for _, delete := range cleanup {
+			file := filepath.Join(c.bundlePath(), delete)
+			os.Remove(file)
+		}
 	}
 
 	return c.save()
 }
 
+func (c *Container) importCheckpoint(input string) (err error) {
+	archiveFile, err := os.Open(input)
+	if err != nil {
+		return errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
+	}
+
+	defer archiveFile.Close()
+	options := &archive.TarOptions{
+		ExcludePatterns: []string{
+			// config.dump and spec.dump are only required
+			// container creation
+			"config.dump",
+			"spec.dump",
+		},
+	}
+	err = archive.Untar(archiveFile, c.bundlePath(), options)
+	if err != nil {
+		return errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input)
+	}
+
+	// Make sure the newly created config.json exists on disk
+	g := generate.NewFromSpec(c.config.Spec)
+	if err = c.saveSpec(g.Spec()); err != nil {
+		return errors.Wrap(err, "Saving imported container specification for restore failed")
+	}
+
+	return nil
+}
+
 func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (err error) {
 
 	if err := c.checkpointRestoreSupported(); err != nil {
@@ -580,6 +662,12 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
 		return errors.Wrapf(ErrCtrStateInvalid, "container %s is running or paused, cannot restore", c.ID())
 	}
 
+	if options.TargetFile != "" {
+		if err = c.importCheckpoint(options.TargetFile); err != nil {
+			return err
+		}
+	}
+
 	// Let's try to stat() CRIU's inventory file. If it does not exist, it makes
 	// no sense to try a restore. This is a minimal check if a checkpoint exist.
 	if _, err := os.Stat(filepath.Join(c.CheckpointPath(), "inventory.img")); os.IsNotExist(err) {
@@ -593,7 +681,13 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
 	// Read network configuration from checkpoint
 	// Currently only one interface with one IP is supported.
 	networkStatusFile, err := os.Open(filepath.Join(c.bundlePath(), "network.status"))
-	if err == nil {
+	// If the restored container should get a new name, the IP address of
+	// the container will not be restored. This assumes that if a new name is
+	// specified, the container is restored multiple times.
+	// TODO: This implicit restoring with or without IP depending on an
+	//       unrelated restore parameter (--name) does not seem like the
+	//       best solution.
+	if err == nil && options.Name == "" {
 		// The file with the network.status does exist. Let's restore the
 		// container with the same IP address as during checkpointing.
 		defer networkStatusFile.Close()
@@ -637,23 +731,44 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
 		return err
 	}
 
+	// Restoring from an import means that we are doing migration
+	if options.TargetFile != "" {
+		g.SetRootPath(c.state.Mountpoint)
+	}
+
 	// We want to have the same network namespace as before.
 	if c.config.CreateNetNS {
 		g.AddOrReplaceLinuxNamespace(spec.NetworkNamespace, c.state.NetNS.Path())
 	}
 
-	// Save the OCI spec to disk
-	if err := c.saveSpec(g.Spec()); err != nil {
-		return err
-	}
-
 	if err := c.makeBindMounts(); err != nil {
 		return err
 	}
 
+	if options.TargetFile != "" {
+		for dstPath, srcPath := range c.state.BindMounts {
+			newMount := spec.Mount{
+				Type:        "bind",
+				Source:      srcPath,
+				Destination: dstPath,
+				Options:     []string{"bind", "private"},
+			}
+			if c.IsReadOnly() && dstPath != "/dev/shm" {
+				newMount.Options = append(newMount.Options, "ro", "nosuid", "noexec", "nodev")
+			}
+			if !MountExists(g.Mounts(), dstPath) {
+				g.AddMount(newMount)
+			}
+		}
+	}
+
 	// Cleanup for a working restore.
 	c.removeConmonFiles()
 
+	// Save the OCI spec to disk
+	if err := c.saveSpec(g.Spec()); err != nil {
+		return err
+	}
 	if err := c.runtime.ociRuntime.createContainer(c, c.config.CgroupParent, &options); err != nil {
 		return err
 	}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 0c8d3edab3..cf1f5701df 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -14,6 +14,7 @@ import (
 	"github.com/containers/storage"
 	"github.com/containers/storage/pkg/stringid"
 	spec "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/opencontainers/runtime-tools/generate"
 	opentracing "github.com/opentracing/opentracing-go"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
@@ -34,7 +35,7 @@ type CtrCreateOption func(*Container) error
 // A true return will include the container, a false return will exclude it.
 type ContainerFilter func(*Container) bool
 
-// NewContainer creates a new container from a given OCI config
+// NewContainer creates a new container from a given OCI config.
 func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (c *Container, err error) {
 	r.lock.Lock()
 	defer r.lock.Unlock()
@@ -44,20 +45,46 @@ func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ..
 	return r.newContainer(ctx, rSpec, options...)
 }
 
-func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (c *Container, err error) {
-	span, _ := opentracing.StartSpanFromContext(ctx, "newContainer")
-	span.SetTag("type", "runtime")
-	defer span.Finish()
+// RestoreContainer re-creates a container from an imported checkpoint
+func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config *ContainerConfig) (c *Container, err error) {
+	r.lock.Lock()
+	defer r.lock.Unlock()
+	if !r.valid {
+		return nil, ErrRuntimeStopped
+	}
 
+	ctr, err := r.initContainerVariables(rSpec, config)
+	if err != nil {
+		return nil, errors.Wrapf(err, "error initializing container variables")
+	}
+	return r.setupContainer(ctx, ctr, true)
+}
+
+func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConfig) (c *Container, err error) {
 	if rSpec == nil {
 		return nil, errors.Wrapf(ErrInvalidArg, "must provide a valid runtime spec to create container")
 	}
-
 	ctr := new(Container)
 	ctr.config = new(ContainerConfig)
 	ctr.state = new(ContainerState)
 
-	ctr.config.ID = stringid.GenerateNonCryptoID()
+	if config == nil {
+		ctr.config.ID = stringid.GenerateNonCryptoID()
+		ctr.config.ShmSize = DefaultShmSize
+	} else {
+		// This is a restore from an imported checkpoint
+		if err := JSONDeepCopy(config, ctr.config); err != nil {
+			return nil, errors.Wrapf(err, "error copying container config for restore")
+		}
+		// If the ID is empty a new name for the restored container was requested
+		if ctr.config.ID == "" {
+			ctr.config.ID = stringid.GenerateNonCryptoID()
+			// Fixup ExitCommand with new ID
+			ctr.config.ExitCommand[len(ctr.config.ExitCommand)-1] = ctr.config.ID
+		}
+		// Reset the log path to point to the default
+		ctr.config.LogPath = ""
+	}
 
 	ctr.config.Spec = new(spec.Spec)
 	if err := JSONDeepCopy(rSpec, ctr.config.Spec); err != nil {
@@ -65,8 +92,6 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
 	}
 	ctr.config.CreatedTime = time.Now()
 
-	ctr.config.ShmSize = DefaultShmSize
-
 	ctr.state.BindMounts = make(map[string]string)
 
 	ctr.config.StopTimeout = CtrRemoveTimeout
@@ -80,12 +105,29 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
 	}
 
 	ctr.runtime = r
+
+	return ctr, nil
+}
+
+func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (c *Container, err error) {
+	span, _ := opentracing.StartSpanFromContext(ctx, "newContainer")
+	span.SetTag("type", "runtime")
+	defer span.Finish()
+
+	ctr, err := r.initContainerVariables(rSpec, nil)
+	if err != nil {
+		return nil, errors.Wrapf(err, "error initializing container variables")
+	}
+
 	for _, option := range options {
 		if err := option(ctr); err != nil {
 			return nil, errors.Wrapf(err, "error running container create option")
 		}
 	}
+	return r.setupContainer(ctx, ctr, false)
+}
 
+func (r *Runtime) setupContainer(ctx context.Context, ctr *Container, restore bool) (c *Container, err error) {
 	// Allocate a lock for the container
 	lock, err := r.lockManager.AllocateLock()
 	if err != nil {
@@ -154,6 +196,19 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
 		return nil, errors.Wrapf(ErrInvalidArg, "unsupported CGroup manager: %s - cannot validate cgroup parent", r.config.CgroupManager)
 	}
 
+	if restore {
+		// Remove information about bind mount
+		// for new container from imported checkpoint
+		g := generate.Generator{Config: ctr.config.Spec}
+		g.RemoveMount("/dev/shm")
+		ctr.config.ShmDir = ""
+		g.RemoveMount("/etc/resolv.conf")
+		g.RemoveMount("/etc/hostname")
+		g.RemoveMount("/etc/hosts")
+		g.RemoveMount("/run/.containerenv")
+		g.RemoveMount("/run/secrets")
+	}
+
 	// Set up storage for the container
 	if err := ctr.setupStorage(ctx); err != nil {
 		return nil, err
diff --git a/pkg/adapter/checkpoint_restore.go b/pkg/adapter/checkpoint_restore.go
new file mode 100644
index 0000000000..97ba5ecf72
--- /dev/null
+++ b/pkg/adapter/checkpoint_restore.go
@@ -0,0 +1,145 @@
+// +build !remoteclient
+
+package adapter
+
+import (
+	"context"
+	"github.com/containers/libpod/libpod"
+	"github.com/containers/libpod/libpod/image"
+	"github.com/containers/storage/pkg/archive"
+	jsoniter "github.com/json-iterator/go"
+	spec "github.com/opencontainers/runtime-spec/specs-go"
+	"github.com/pkg/errors"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+)
+
+// Prefixing the checkpoint/restore related functions with 'cr'
+
+// crImportFromJSON imports the JSON files stored in the exported
+// checkpoint tarball
+func crImportFromJSON(filePath string, v interface{}) error {
+	jsonFile, err := os.Open(filePath)
+	if err != nil {
+		return errors.Wrapf(err, "Failed to open container definition %s for restore", filePath)
+	}
+	defer jsonFile.Close()
+
+	content, err := ioutil.ReadAll(jsonFile)
+	if err != nil {
+		return errors.Wrapf(err, "Failed to read container definition %s for restore", filePath)
+	}
+	json := jsoniter.ConfigCompatibleWithStandardLibrary
+	if err = json.Unmarshal([]byte(content), v); err != nil {
+		return errors.Wrapf(err, "Failed to unmarshal container definition %s for restore", filePath)
+	}
+
+	return nil
+}
+
+// crImportCheckpoint it the function which imports the information
+// from checkpoint tarball and re-creates the container from that information
+func crImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input string, name string) ([]*libpod.Container, error) {
+	// First get the container definition from the
+	// tarball to a temporary directory
+	archiveFile, err := os.Open(input)
+	if err != nil {
+		return nil, errors.Wrapf(err, "Failed to open checkpoint archive %s for import", input)
+	}
+	defer archiveFile.Close()
+	options := &archive.TarOptions{
+		// Here we only need the files config.dump and spec.dump
+		ExcludePatterns: []string{
+			"checkpoint",
+			"artifacts",
+			"ctr.log",
+			"network.status",
+		},
+	}
+	dir, err := ioutil.TempDir("", "checkpoint")
+	if err != nil {
+		return nil, err
+	}
+	defer os.RemoveAll(dir)
+	err = archive.Untar(archiveFile, dir, options)
+	if err != nil {
+		return nil, errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input)
+	}
+
+	// Load spec.dump from temporary directory
+	spec := new(spec.Spec)
+	if err := crImportFromJSON(filepath.Join(dir, "spec.dump"), spec); err != nil {
+		return nil, err
+	}
+
+	// Load config.dump from temporary directory
+	config := new(libpod.ContainerConfig)
+	if err = crImportFromJSON(filepath.Join(dir, "config.dump"), config); err != nil {
+		return nil, err
+	}
+
+	// This should not happen as checkpoints with these options are not exported.
+	if (len(config.Dependencies) > 0) || (len(config.NamedVolumes) > 0) {
+		return nil, errors.Errorf("Cannot import checkpoints of containers with named volumes or dependencies")
+	}
+
+	ctrID := config.ID
+	newName := false
+
+	// Check if the restored container gets a new name
+	if name != "" {
+		config.ID = ""
+		config.Name = name
+		newName = true
+	}
+
+	ctrName := config.Name
+
+	// The code to load the images is copied from create.go
+	var writer io.Writer
+	// In create.go this only set if '--quiet' does not exist.
+	writer = os.Stderr
+	rtc, err := runtime.GetConfig()
+	if err != nil {
+		return nil, err
+	}
+
+	_, err = runtime.ImageRuntime().New(ctx, config.RootfsImageName, rtc.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, false, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	// Now create a new container from the just loaded information
+	container, err := runtime.RestoreContainer(ctx, spec, config)
+	if err != nil {
+		return nil, err
+	}
+
+	var containers []*libpod.Container
+	if container == nil {
+		return nil, nil
+	}
+
+	containerConfig := container.Config()
+	if containerConfig.Name != ctrName {
+		return nil, errors.Errorf("Name of restored container (%s) does not match requested name (%s)", containerConfig.Name, ctrName)
+	}
+
+	if newName == false {
+		// Only check ID for a restore with the same name.
+		// Using -n to request a new name for the restored container, will also create a new ID
+		if containerConfig.ID != ctrID {
+			return nil, errors.Errorf("ID of restored container (%s) does not match requested ID (%s)", containerConfig.ID, ctrID)
+		}
+	}
+
+	// Check if the ExitCommand points to the correct container ID
+	if containerConfig.ExitCommand[len(containerConfig.ExitCommand)-1] != containerConfig.ID {
+		return nil, errors.Errorf("'ExitCommandID' uses ID %s instead of container ID %s", containerConfig.ExitCommand[len(containerConfig.ExitCommand)-1], containerConfig.ID)
+	}
+
+	containers = append(containers, container)
+	return containers, nil
+}
diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go
index 34ee70d3dd..29297fbd53 100644
--- a/pkg/adapter/containers.go
+++ b/pkg/adapter/containers.go
@@ -526,7 +526,7 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues, options libpod.
 }
 
 // Restore one or more containers
-func (r *LocalRuntime) Restore(c *cliconfig.RestoreValues, options libpod.ContainerCheckpointOptions) error {
+func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues, options libpod.ContainerCheckpointOptions) error {
 	var (
 		containers     []*libpod.Container
 		err, lastError error
@@ -538,7 +538,9 @@ func (r *LocalRuntime) Restore(c *cliconfig.RestoreValues, options libpod.Contai
 		return state == libpod.ContainerStateExited
 	})
 
-	if c.All {
+	if c.Import != "" {
+		containers, err = crImportCheckpoint(ctx, r.Runtime, c.Import, c.Name)
+	} else if c.All {
 		containers, err = r.GetContainers(filterFuncs...)
 	} else {
 		containers, err = shortcuts.GetContainersByContext(false, c.Latest, c.InputArgs, r.Runtime)
diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go
index bc6a9cfcd3..776fcbb706 100644
--- a/pkg/adapter/containers_remote.go
+++ b/pkg/adapter/containers_remote.go
@@ -664,6 +664,10 @@ func (r *LocalRuntime) Attach(ctx context.Context, c *cliconfig.AttachValues) er
 
 // Checkpoint one or more containers
 func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues, options libpod.ContainerCheckpointOptions) error {
+	if c.Export != "" {
+		return errors.New("the remote client does not support exporting checkpoints")
+	}
+
 	var lastError error
 	ids, err := iopodman.GetContainersByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
 	if err != nil {
@@ -699,7 +703,11 @@ func (r *LocalRuntime) Checkpoint(c *cliconfig.CheckpointValues, options libpod.
 }
 
 // Restore one or more containers
-func (r *LocalRuntime) Restore(c *cliconfig.RestoreValues, options libpod.ContainerCheckpointOptions) error {
+func (r *LocalRuntime) Restore(ctx context.Context, c *cliconfig.RestoreValues, options libpod.ContainerCheckpointOptions) error {
+	if c.Import != "" {
+		return errors.New("the remote client does not support importing checkpoints")
+	}
+
 	var lastError error
 	ids, err := iopodman.GetContainersByContext().Call(r.Conn, c.All, c.Latest, c.InputArgs)
 	if err != nil {
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 95ec214339..d452a062b9 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -347,4 +347,49 @@ var _ = Describe("Podman checkpoint", func() {
 		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
 	})
 
+	// This test does the same steps which are necessary for migrating
+	// a container from one host to another
+	It("podman checkpoint container with export (migration)", func() {
+		// CRIU does not work with seccomp correctly on RHEL7
+		session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "seccomp=unconfined", "-d", ALPINE, "top"})
+		session.WaitWithDefaultTimeout()
+		Expect(session.ExitCode()).To(Equal(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+
+		result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", "/tmp/checkpoint.tar.gz"})
+		result.WaitWithDefaultTimeout()
+
+		Expect(result.ExitCode()).To(Equal(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+		Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+		// Remove all containers to simulate migration
+		result = podmanTest.Podman([]string{"rm", "-fa"})
+		result.WaitWithDefaultTimeout()
+		Expect(result.ExitCode()).To(Equal(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+		result = podmanTest.Podman([]string{"container", "restore", "-i", "/tmp/checkpoint.tar.gz"})
+		result.WaitWithDefaultTimeout()
+
+		Expect(result.ExitCode()).To(Equal(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+		Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+		// Restore container a second time with different name
+		result = podmanTest.Podman([]string{"container", "restore", "-i", "/tmp/checkpoint.tar.gz", "-n", "restore_again"})
+		result.WaitWithDefaultTimeout()
+
+		Expect(result.ExitCode()).To(Equal(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2))
+		Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+		result = podmanTest.Podman([]string{"rm", "-fa"})
+		result.WaitWithDefaultTimeout()
+		Expect(result.ExitCode()).To(Equal(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+		// Remove exported checkpoint
+		os.Remove("/tmp/checkpoint.tar.gz")
+	})
 })