diff --git a/integration-cli/docker_api_volumes_test.go b/integration-cli/docker_api_volumes_test.go
deleted file mode 100644
index 65a9652092..0000000000
--- a/integration-cli/docker_api_volumes_test.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"path/filepath"
-	"strings"
-	"time"
-
-	"github.com/docker/docker/api/types/filters"
-	volumetypes "github.com/docker/docker/api/types/volume"
-	"github.com/docker/docker/client"
-	"github.com/docker/docker/integration-cli/checker"
-	"github.com/go-check/check"
-	"golang.org/x/net/context"
-)
-
-func (s *DockerSuite) TestVolumesAPIList(c *check.C) {
-	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
-	cid, _ := dockerCmd(c, "run", "-d", "-v", prefix+"/foo", "busybox")
-
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
-	defer cli.Close()
-
-	container, err := cli.ContainerInspect(context.Background(), strings.TrimSpace(cid))
-	c.Assert(err, checker.IsNil)
-	vname := container.Mounts[0].Name
-
-	volumes, err := cli.VolumeList(context.Background(), filters.Args{})
-	c.Assert(err, checker.IsNil)
-
-	found := false
-	for _, vol := range volumes.Volumes {
-		if vol.Name == vname {
-			found = true
-			break
-		}
-	}
-	c.Assert(found, checker.Equals, true)
-}
-
-func (s *DockerSuite) TestVolumesAPICreate(c *check.C) {
-	config := volumetypes.VolumesCreateBody{
-		Name: "test",
-	}
-
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
-	defer cli.Close()
-
-	vol, err := cli.VolumeCreate(context.Background(), config)
-	c.Assert(err, check.IsNil)
-
-	c.Assert(filepath.Base(filepath.Dir(vol.Mountpoint)), checker.Equals, config.Name)
-}
-
-func (s *DockerSuite) TestVolumesAPIRemove(c *check.C) {
-	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
-	cid, _ := dockerCmd(c, "run", "-d", "-v", prefix+"/foo", "--name=test", "busybox")
-
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
-	defer cli.Close()
-
-	container, err := cli.ContainerInspect(context.Background(), strings.TrimSpace(cid))
-	c.Assert(err, checker.IsNil)
-	vname := container.Mounts[0].Name
-
-	err = cli.VolumeRemove(context.Background(), vname, false)
-	c.Assert(err.Error(), checker.Contains, "volume is in use")
-
-	dockerCmd(c, "rm", "-f", "test")
-	err = cli.VolumeRemove(context.Background(), vname, false)
-	c.Assert(err, checker.IsNil)
-}
-
-func (s *DockerSuite) TestVolumesAPIInspect(c *check.C) {
-	config := volumetypes.VolumesCreateBody{
-		Name: "test",
-	}
-
-	// sampling current time minus a minute so to now have false positive in case of delays
-	now := time.Now().Truncate(time.Minute)
-
-	cli, err := client.NewEnvClient()
-	c.Assert(err, checker.IsNil)
-	defer cli.Close()
-
-	_, err = cli.VolumeCreate(context.Background(), config)
-	c.Assert(err, check.IsNil)
-
-	vol, err := cli.VolumeInspect(context.Background(), config.Name)
-	c.Assert(err, checker.IsNil)
-	c.Assert(vol.Name, checker.Equals, config.Name)
-
-	// comparing CreatedAt field time for the new volume to now. Removing a minute from both to avoid false positive
-	testCreatedAt, err := time.Parse(time.RFC3339, strings.TrimSpace(vol.CreatedAt))
-	c.Assert(err, check.IsNil)
-	testCreatedAt = testCreatedAt.Truncate(time.Minute)
-	if !testCreatedAt.Equal(now) {
-		c.Assert(fmt.Errorf("Time Volume is CreatedAt not equal to current time"), check.NotNil)
-	}
-}
diff --git a/integration/internal/container/ops.go b/integration/internal/container/ops.go
index e3d538bef1..9360527d37 100644
--- a/integration/internal/container/ops.go
+++ b/integration/internal/container/ops.go
@@ -1,6 +1,8 @@
 package container
 
 import (
+	"fmt"
+
 	containertypes "github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/strslice"
 	"github.com/docker/go-connections/nat"
@@ -57,3 +59,20 @@ func WithWorkingDir(dir string) func(*TestContainerConfig) {
 		c.Config.WorkingDir = dir
 	}
 }
+
+// WithVolume sets the volume of the container
+func WithVolume(name string) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		if c.Config.Volumes == nil {
+			c.Config.Volumes = map[string]struct{}{}
+		}
+		c.Config.Volumes[name] = struct{}{}
+	}
+}
+
+// WithBind sets the bind mount of the container
+func WithBind(src, target string) func(*TestContainerConfig) {
+	return func(c *TestContainerConfig) {
+		c.HostConfig.Binds = append(c.HostConfig.Binds, fmt.Sprintf("%s:%s", src, target))
+	}
+}
diff --git a/integration/volume/main_test.go b/integration/volume/main_test.go
new file mode 100644
index 0000000000..206f7377ae
--- /dev/null
+++ b/integration/volume/main_test.go
@@ -0,0 +1,33 @@
+package volume // import "github.com/docker/docker/integration/volume"
+
+import (
+	"fmt"
+	"os"
+	"testing"
+
+	"github.com/docker/docker/internal/test/environment"
+)
+
+var testEnv *environment.Execution
+
+func TestMain(m *testing.M) {
+	var err error
+	testEnv, err = environment.New()
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+	err = environment.EnsureFrozenImagesLinux(testEnv)
+	if err != nil {
+		fmt.Println(err)
+		os.Exit(1)
+	}
+
+	testEnv.Print()
+	os.Exit(m.Run())
+}
+
+func setupTest(t *testing.T) func() {
+	environment.ProtectAll(t, testEnv)
+	return func() { testEnv.Clean(t) }
+}
diff --git a/integration/volume/volume_test.go b/integration/volume/volume_test.go
new file mode 100644
index 0000000000..38ce5782e1
--- /dev/null
+++ b/integration/volume/volume_test.go
@@ -0,0 +1,115 @@
+package volume
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/filters"
+	volumetypes "github.com/docker/docker/api/types/volume"
+	"github.com/docker/docker/integration/internal/container"
+	"github.com/docker/docker/integration/internal/request"
+	"github.com/docker/docker/internal/testutil"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestVolumesCreateAndList(t *testing.T) {
+	defer setupTest(t)()
+	client := request.NewAPIClient(t)
+	ctx := context.Background()
+
+	name := t.Name()
+	vol, err := client.VolumeCreate(ctx, volumetypes.VolumesCreateBody{
+		Name: name,
+	})
+	require.NoError(t, err)
+
+	expected := types.Volume{
+		// Ignore timestamp of CreatedAt
+		CreatedAt:  vol.CreatedAt,
+		Driver:     "local",
+		Scope:      "local",
+		Name:       name,
+		Options:    map[string]string{},
+		Mountpoint: fmt.Sprintf("%s/volumes/%s/_data", testEnv.DaemonInfo.DockerRootDir, name),
+	}
+	assert.Equal(t, vol, expected)
+
+	volumes, err := client.VolumeList(ctx, filters.Args{})
+	require.NoError(t, err)
+
+	assert.Equal(t, len(volumes.Volumes), 1)
+	assert.NotNil(t, volumes.Volumes[0])
+	assert.Equal(t, *volumes.Volumes[0], expected)
+}
+
+func TestVolumesRemove(t *testing.T) {
+	defer setupTest(t)()
+	client := request.NewAPIClient(t)
+	ctx := context.Background()
+
+	prefix, _ := getPrefixAndSlashFromDaemonPlatform()
+
+	id := container.Create(t, ctx, client, container.WithVolume(prefix+"foo"))
+
+	c, err := client.ContainerInspect(ctx, id)
+	require.NoError(t, err)
+	vname := c.Mounts[0].Name
+
+	err = client.VolumeRemove(ctx, vname, false)
+	testutil.ErrorContains(t, err, "volume is in use")
+
+	err = client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{
+		Force: true,
+	})
+	require.NoError(t, err)
+
+	err = client.VolumeRemove(ctx, vname, false)
+	require.NoError(t, err)
+}
+
+func TestVolumesInspect(t *testing.T) {
+	defer setupTest(t)()
+	client := request.NewAPIClient(t)
+	ctx := context.Background()
+
+	// sampling current time minus a minute so to now have false positive in case of delays
+	now := time.Now().Truncate(time.Minute)
+
+	name := t.Name()
+	_, err := client.VolumeCreate(ctx, volumetypes.VolumesCreateBody{
+		Name: name,
+	})
+	require.NoError(t, err)
+
+	vol, err := client.VolumeInspect(ctx, name)
+	require.NoError(t, err)
+
+	expected := types.Volume{
+		// Ignore timestamp of CreatedAt
+		CreatedAt:  vol.CreatedAt,
+		Driver:     "local",
+		Scope:      "local",
+		Name:       name,
+		Options:    map[string]string{},
+		Mountpoint: fmt.Sprintf("%s/volumes/%s/_data", testEnv.DaemonInfo.DockerRootDir, name),
+	}
+	assert.Equal(t, vol, expected)
+
+	// comparing CreatedAt field time for the new volume to now. Removing a minute from both to avoid false positive
+	testCreatedAt, err := time.Parse(time.RFC3339, strings.TrimSpace(vol.CreatedAt))
+	require.NoError(t, err)
+	testCreatedAt = testCreatedAt.Truncate(time.Minute)
+	assert.Equal(t, testCreatedAt.Equal(now), true, "Time Volume is CreatedAt not equal to current time")
+}
+
+func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
+	if testEnv.OSType == "windows" {
+		return "c:", `\`
+	}
+	return "", "/"
+}