diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index d34d69b1ee..e4aead7fa3 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -5,6 +5,7 @@ import (
 	"errors"
 	"fmt"
 	"os"
+	"reflect"
 	"strconv"
 	"sync"
 	"time"
@@ -1110,6 +1111,44 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta
 		fmt.Fprintf(os.Stderr, "%s\n", w)
 	}
 
+	if opts.Spec != nil && !reflect.ValueOf(opts.Spec).IsNil() {
+		// If this is a checkpoint image, restore it.
+		img, resolvedImageName := opts.Spec.GetImage()
+		if img != nil && resolvedImageName != "" {
+			imgData, err := img.Inspect(ctx, nil)
+			if err != nil {
+				return nil, err
+			}
+			if imgData != nil {
+				_, isCheckpointImage := imgData.Annotations[define.CheckpointAnnotationRuntimeName]
+				if isCheckpointImage {
+					var restoreOptions entities.RestoreOptions
+					restoreOptions.Name = opts.Spec.Name
+					restoreOptions.Pod = opts.Spec.Pod
+					responses, err := ic.ContainerRestore(ctx, []string{resolvedImageName}, restoreOptions)
+					if err != nil {
+						return nil, err
+					}
+
+					report := entities.ContainerRunReport{}
+					for _, r := range responses {
+						report.Id = r.Id
+						report.ExitCode = 0
+						if r.Err != nil {
+							logrus.Errorf("Failed to restore checkpoint image %s: %v", resolvedImageName, r.Err)
+							report.ExitCode = 126
+						}
+						if r.RawInput != "" {
+							logrus.Errorf("Failed to restore checkpoint image %s: %v", resolvedImageName, r.RawInput)
+							report.ExitCode = 126
+						}
+					}
+					return &report, nil
+				}
+			}
+		}
+	}
+
 	rtSpec, spec, optsN, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec, false, nil)
 	if err != nil {
 		return nil, err
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index f3e66982d8..e8a09a7559 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"io"
 	"os"
+	"reflect"
 	"strconv"
 	"strings"
 	"sync"
@@ -790,6 +791,30 @@ func (ic *ContainerEngine) ContainerListExternal(ctx context.Context) ([]entitie
 }
 
 func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) {
+	if opts.Spec != nil && !reflect.ValueOf(opts.Spec).IsNil() && opts.Spec.RawImageName != "" {
+		// If this is a checkpoint image, restore it.
+		getImageOptions := new(images.GetOptions).WithSize(false)
+		inspectReport, err := images.GetImage(ic.ClientCtx, opts.Spec.RawImageName, getImageOptions)
+		if err != nil {
+			return nil, fmt.Errorf("no such container or image: %s", opts.Spec.RawImageName)
+		}
+		if inspectReport != nil {
+			_, isCheckpointImage := inspectReport.Annotations[define.CheckpointAnnotationRuntimeName]
+			if isCheckpointImage {
+				restoreOptions := new(containers.RestoreOptions)
+				restoreOptions.WithName(opts.Spec.Name)
+				restoreOptions.WithPod(opts.Spec.Pod)
+
+				restoreReport, err := containers.Restore(ic.ClientCtx, inspectReport.ID, restoreOptions)
+				if err != nil {
+					return nil, err
+				}
+				runReport := entities.ContainerRunReport{Id: restoreReport.Id}
+				return &runReport, nil
+			}
+		}
+	}
+
 	con, err := containers.CreateWithSpec(ic.ClientCtx, opts.Spec, nil)
 	if err != nil {
 		return nil, err
diff --git a/test/e2e/checkpoint_image_test.go b/test/e2e/checkpoint_image_test.go
new file mode 100644
index 0000000000..6558d78c64
--- /dev/null
+++ b/test/e2e/checkpoint_image_test.go
@@ -0,0 +1,343 @@
+package integration
+
+import (
+	"os"
+	"os/exec"
+	"strconv"
+	"strings"
+
+	"github.com/containers/podman/v4/pkg/criu"
+	. "github.com/containers/podman/v4/test/utils"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	. "github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe("Podman checkpoint", func() {
+	var (
+		tempdir    string
+		err        error
+		podmanTest *PodmanTestIntegration
+	)
+
+	BeforeEach(func() {
+		SkipIfContainerized("FIXME: #15015. All checkpoint tests hang when containerized.")
+		SkipIfRootless("checkpoint not supported in rootless mode")
+		tempdir, err = CreateTempDirInTempDir()
+		if err != nil {
+			os.Exit(1)
+		}
+		podmanTest = PodmanTestCreate(tempdir)
+		podmanTest.Setup()
+		// Check if the runtime implements checkpointing. Currently only
+		// runc's checkpoint/restore implementation is supported.
+		cmd := exec.Command(podmanTest.OCIRuntime, "checkpoint", "--help")
+		if err := cmd.Start(); err != nil {
+			Skip("OCI runtime does not support checkpoint/restore")
+		}
+		if err := cmd.Wait(); err != nil {
+			Skip("OCI runtime does not support checkpoint/restore")
+		}
+
+		if !criu.CheckForCriu(criu.MinCriuVersion) {
+			Skip("CRIU is missing or too old.")
+		}
+	})
+
+	AfterEach(func() {
+		podmanTest.Cleanup()
+		f := CurrentGinkgoTestDescription()
+		processTestResult(f)
+	})
+
+	It("podman checkpoint --create-image with bogus container", func() {
+		checkpointImage := "foobar-checkpoint"
+		session := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "foobar"})
+		session.WaitWithDefaultTimeout()
+		Expect(session).To(ExitWithError())
+		Expect(session.ErrorToString()).To(ContainSubstring("no container with name or ID \"foobar\" found"))
+	})
+
+	It("podman checkpoint --create-image with running container", func() {
+		// Container image must be lowercase
+		checkpointImage := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
+		containerName := "alpine-container-" + RandomString(6)
+
+		localRunString := []string{
+			"run",
+			"-it",
+			"-d",
+			"--ip", GetRandomIPAddress(),
+			"--name", containerName,
+			ALPINE,
+			"top",
+		}
+		session := podmanTest.Podman(localRunString)
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		containerID := session.OutputToString()
+
+		// Checkpoint image should not exist
+		session = podmanTest.Podman([]string{"images"})
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeFalse())
+
+		// Check if none of the checkpoint/restore specific information is displayed
+		// for newly started containers.
+		inspect := podmanTest.Podman([]string{"inspect", containerID})
+		inspect.WaitWithDefaultTimeout()
+		Expect(inspect).Should(Exit(0))
+		inspectOut := inspect.InspectContainerToJSON()
+		Expect(inspectOut[0].State.Checkpointed).To(BeFalse(), ".State.Checkpointed")
+		Expect(inspectOut[0].State.Restored).To(BeFalse(), ".State.Restored")
+		Expect(inspectOut[0].State).To(HaveField("CheckpointPath", ""))
+		Expect(inspectOut[0].State).To(HaveField("CheckpointLog", ""))
+		Expect(inspectOut[0].State).To(HaveField("RestoreLog", ""))
+
+		result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "--keep", containerID})
+		result.WaitWithDefaultTimeout()
+
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+		Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+		inspect = podmanTest.Podman([]string{"inspect", containerID})
+		inspect.WaitWithDefaultTimeout()
+		Expect(inspect).Should(Exit(0))
+		inspectOut = inspect.InspectContainerToJSON()
+		Expect(inspectOut[0].State.Checkpointed).To(BeTrue(), ".State.Checkpointed")
+		Expect(inspectOut[0].State.CheckpointPath).To(ContainSubstring("userdata/checkpoint"))
+		Expect(inspectOut[0].State.CheckpointLog).To(ContainSubstring("userdata/dump.log"))
+
+		// Check if checkpoint image has been created
+		session = podmanTest.Podman([]string{"images"})
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeTrue())
+
+		// Check if the checkpoint image contains annotations
+		inspect = podmanTest.Podman([]string{"inspect", checkpointImage})
+		inspect.WaitWithDefaultTimeout()
+		Expect(inspect).Should(Exit(0))
+		inspectImageOut := inspect.InspectImageJSON()
+		Expect(inspectImageOut[0].Annotations["io.podman.annotations.checkpoint.name"]).To(
+			BeEquivalentTo(containerName),
+			"io.podman.annotations.checkpoint.name",
+		)
+
+		ociRuntimeName := ""
+		if strings.Contains(podmanTest.OCIRuntime, "runc") {
+			ociRuntimeName = "runc"
+		} else if strings.Contains(podmanTest.OCIRuntime, "crun") {
+			ociRuntimeName = "crun"
+		}
+		if ociRuntimeName != "" {
+			Expect(inspectImageOut[0].Annotations["io.podman.annotations.checkpoint.runtime.name"]).To(
+				BeEquivalentTo(ociRuntimeName),
+				"io.podman.annotations.checkpoint.runtime.name",
+			)
+		}
+
+		// Remove existing container
+		result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerID})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+
+		// Restore container from checkpoint image
+		result = podmanTest.Podman([]string{"container", "restore", checkpointImage})
+		result.WaitWithDefaultTimeout()
+
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+		Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+
+		// Clean-up
+		result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+		result = podmanTest.Podman([]string{"rmi", checkpointImage})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+	})
+
+	It("podman restore multiple containers from single checkpoint image", func() {
+		// Container image must be lowercase
+		checkpointImage := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
+		containerName := "alpine-container-" + RandomString(6)
+
+		localRunString := []string{"run", "-d", "--name", containerName, ALPINE, "top"}
+		session := podmanTest.Podman(localRunString)
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		containerID := session.OutputToString()
+
+		// Checkpoint image should not exist
+		session = podmanTest.Podman([]string{"images"})
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeFalse())
+
+		result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "--keep", containerID})
+		result.WaitWithDefaultTimeout()
+
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+		Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
+
+		// Check if checkpoint image has been created
+		session = podmanTest.Podman([]string{"images"})
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeTrue())
+
+		// Remove existing container
+		result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerID})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+
+		for i := 1; i < 5; i++ {
+			// Restore container from checkpoint image
+			name := containerName + strconv.Itoa(i)
+			result = podmanTest.Podman([]string{"container", "restore", "--name", name, checkpointImage})
+			result.WaitWithDefaultTimeout()
+			Expect(result).Should(Exit(0))
+			Expect(podmanTest.NumberOfContainersRunning()).To(Equal(i))
+
+			// Check that the container is running
+			status := podmanTest.Podman([]string{"inspect", name, "--format={{.State.Status}}"})
+			status.WaitWithDefaultTimeout()
+			Expect(status).Should(Exit(0))
+			Expect(status.OutputToString()).To(Equal("running"))
+		}
+
+		// Clean-up
+		result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+		result = podmanTest.Podman([]string{"rmi", checkpointImage})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+	})
+
+	It("podman restore multiple containers from multiple checkpoint images", func() {
+		// Container image must be lowercase
+		checkpointImage1 := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
+		checkpointImage2 := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
+		containerName1 := "alpine-container-" + RandomString(6)
+		containerName2 := "alpine-container-" + RandomString(6)
+
+		// Create first container
+		localRunString := []string{"run", "-d", "--name", containerName1, ALPINE, "top"}
+		session := podmanTest.Podman(localRunString)
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		containerID1 := session.OutputToString()
+
+		// Create second container
+		localRunString = []string{"run", "-d", "--name", containerName2, ALPINE, "top"}
+		session = podmanTest.Podman(localRunString)
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		containerID2 := session.OutputToString()
+
+		// Checkpoint first container
+		result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage1, "--keep", containerID1})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+
+		// Checkpoint second container
+		result = podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage2, "--keep", containerID2})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+		// Remove existing containers
+		result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerName1, containerName2})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+
+		// Restore both containers from images
+		result = podmanTest.Podman([]string{"container", "restore", checkpointImage1, checkpointImage2})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2))
+
+		// Check if first container is running
+		status := podmanTest.Podman([]string{"inspect", containerName1, "--format={{.State.Status}}"})
+		status.WaitWithDefaultTimeout()
+		Expect(status).Should(Exit(0))
+		Expect(status.OutputToString()).To(Equal("running"))
+
+		// Check if second container is running
+		status = podmanTest.Podman([]string{"inspect", containerName2, "--format={{.State.Status}}"})
+		status.WaitWithDefaultTimeout()
+		Expect(status).Should(Exit(0))
+		Expect(status.OutputToString()).To(Equal("running"))
+
+		// Clean-up
+		result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+		result = podmanTest.Podman([]string{"rmi", checkpointImage1, checkpointImage2})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+	})
+
+	It("podman run with checkpoint image", func() {
+		// Container image must be lowercase
+		checkpointImage := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
+		containerName := "alpine-container-" + RandomString(6)
+
+		// Create container
+		localRunString := []string{"run", "-d", "--name", containerName, ALPINE, "top"}
+		session := podmanTest.Podman(localRunString)
+		session.WaitWithDefaultTimeout()
+		Expect(session).Should(Exit(0))
+		containerID1 := session.OutputToString()
+
+		// Checkpoint container, create checkpoint image
+		result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "--keep", containerID1})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+		// Remove existing container
+		result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerName})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+
+		// Restore containers from image using `podman run`
+		result = podmanTest.Podman([]string{"run", checkpointImage})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+
+		// Check if the container is running
+		status := podmanTest.Podman([]string{"inspect", containerName, "--format={{.State.Status}}"})
+		status.WaitWithDefaultTimeout()
+		Expect(status).Should(Exit(0))
+		Expect(status.OutputToString()).To(Equal("running"))
+
+		// Clean-up
+		result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+		result = podmanTest.Podman([]string{"rmi", checkpointImage})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+	})
+})