diff --git a/pkg/machine/applehv/claim.go b/pkg/machine/applehv/claim.go
new file mode 100644
index 0000000000..c9a17319f7
--- /dev/null
+++ b/pkg/machine/applehv/claim.go
@@ -0,0 +1,84 @@
+//go:build darwin && arm64
+// +build darwin,arm64
+
+package applehv
+
+import (
+	"fmt"
+	"io"
+	"io/fs"
+	"net"
+	"os"
+	"os/user"
+	"path/filepath"
+	"time"
+)
+
+// TODO the following functions were taken from pkg/qemu/claim_darwin.go and
+// should be refactored.  I'm thinking even something in pkg/machine/
+
+func dockerClaimSupported() bool {
+	return true
+}
+
+func dockerClaimHelperInstalled() bool {
+	u, err := user.Current()
+	if err != nil {
+		return false
+	}
+
+	labelName := fmt.Sprintf("com.github.containers.podman.helper-%s", u.Username)
+	fileName := filepath.Join("/Library", "LaunchDaemons", labelName+".plist")
+	info, err := os.Stat(fileName)
+	return err == nil && info.Mode().IsRegular()
+}
+
+func claimDockerSock() bool {
+	u, err := user.Current()
+	if err != nil {
+		return false
+	}
+
+	helperSock := fmt.Sprintf("/var/run/podman-helper-%s.socket", u.Username)
+	con, err := net.DialTimeout("unix", helperSock, time.Second*5)
+	if err != nil {
+		return false
+	}
+	_ = con.SetWriteDeadline(time.Now().Add(time.Second * 5))
+	_, err = fmt.Fprintln(con, "GO")
+	if err != nil {
+		return false
+	}
+	_ = con.SetReadDeadline(time.Now().Add(time.Second * 5))
+	read, err := io.ReadAll(con)
+
+	return err == nil && string(read) == "OK"
+}
+
+func findClaimHelper() string {
+	exe, err := os.Executable()
+	if err != nil {
+		return ""
+	}
+
+	exe, err = filepath.EvalSymlinks(exe)
+	if err != nil {
+		return ""
+	}
+
+	return filepath.Join(filepath.Dir(exe), "podman-mac-helper")
+}
+
+func checkSockInUse(sock string) bool {
+	if info, err := os.Stat(sock); err == nil && info.Mode()&fs.ModeSocket == fs.ModeSocket {
+		_, err = net.DialTimeout("unix", dockerSock, dockerConnectTimeout)
+		return err == nil
+	}
+
+	return false
+}
+
+func alreadyLinked(target string, link string) bool {
+	read, err := os.Readlink(link)
+	return err == nil && read == target
+}
diff --git a/pkg/machine/applehv/machine.go b/pkg/machine/applehv/machine.go
index 2925adfa54..5bb8cf1ad9 100644
--- a/pkg/machine/applehv/machine.go
+++ b/pkg/machine/applehv/machine.go
@@ -16,6 +16,7 @@ import (
 	"path/filepath"
 	"strconv"
 	"strings"
+	"syscall"
 	"time"
 
 	"github.com/containers/common/pkg/config"
@@ -34,6 +35,12 @@ var (
 	vmtype = machine.AppleHvVirt
 )
 
+const (
+	dockerSock           = "/var/run/docker.sock"
+	dockerConnectTimeout = 5 * time.Second
+	apiUpTimeout         = 20 * time.Second
+)
+
 // VfkitHelper describes the use of vfkit: cmdline and endpoint
 type VfkitHelper struct {
 	LogLevel        logrus.Level
@@ -53,12 +60,6 @@ type MacMachine struct {
 	Mounts []machine.Mount
 	// Name of VM
 	Name string
-	// TODO We will need something like this for applehv but until host networking
-	// is worked out, we cannot be sure what it looks like.
-	/*
-		// NetworkVSock is for the user networking
-		NetworkHVSock machine.HVSockRegistryEntry
-	*/
 	// ReadySocket tells host when vm is booted
 	ReadySocket machine.VMFile
 	// ResourceConfig is physical attrs of the VM
@@ -72,13 +73,17 @@ type MacMachine struct {
 	// LastUp contains the last recorded uptime
 	LastUp time.Time
 	// The VFKit endpoint where we can interact with the VM
-	Vfkit   VfkitHelper
-	LogPath machine.VMFile
+	Vfkit       VfkitHelper
+	LogPath     machine.VMFile
+	GvProxyPid  machine.VMFile
+	GvProxySock machine.VMFile
 }
 
 func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
 	var (
-		key string
+		key          string
+		mounts       []machine.Mount
+		virtiofsMnts []machine.VirtIoFs
 	)
 	dataDir, err := machine.GetDataDir(machine.AppleHvVirt)
 	if err != nil {
@@ -125,12 +130,22 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
 		return false, err
 	}
 	m.LogPath = *logPath
-	runtimeDir, err := getRuntimeDir()
+	runtimeDir, err := m.getRuntimeDir()
 	if err != nil {
 		return false, err
 	}
 
-	readySocket, err := machine.NewMachineFile(filepath.Join(runtimeDir, "podman", fmt.Sprintf("%s_ready.sock", m.Name)), nil)
+	readySocket, err := machine.NewMachineFile(filepath.Join(runtimeDir, fmt.Sprintf("%s_ready.sock", m.Name)), nil)
+	if err != nil {
+		return false, err
+	}
+
+	gvProxyPid, err := machine.NewMachineFile(filepath.Join(runtimeDir, "gvproxy.pid"), nil)
+	if err != nil {
+		return false, err
+	}
+
+	gvProxySock, err := machine.NewMachineFile(filepath.Join(runtimeDir, "gvproxy.sock"), nil)
 	if err != nil {
 		return false, err
 	}
@@ -150,6 +165,8 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
 	}
 
 	m.ReadySocket = *readySocket
+	m.GvProxyPid = *gvProxyPid
+	m.GvProxySock = *gvProxySock
 	m.Vfkit.VirtualMachine.Devices = defaultDevices
 	m.Vfkit.Endpoint = defaultVFKitEndpoint
 	m.Vfkit.VfkitBinaryPath = vfkitBinaryPath
@@ -166,9 +183,20 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
 	}
 	m.Port = sshPort
 
+	for _, volume := range opts.Volumes {
+		source, target, _, readOnly, err := machine.ParseVolumeFromPath(volume)
+		if err != nil {
+			return false, err
+		}
+		mnt := machine.NewVirtIoFsMount(source, target, readOnly)
+		virtiofsMnts = append(virtiofsMnts, mnt)
+		mounts = append(mounts, mnt.ToMount())
+	}
+	m.Mounts = mounts
+
 	if len(opts.IgnitionPath) < 1 {
 		// TODO localhost needs to be restored here
-		uri := machine.SSHRemoteConnection.MakeSSHURL("192.168.64.3", fmt.Sprintf("/run/user/%d/podman/podman.sock", m.UID), strconv.Itoa(m.Port), m.RemoteUsername)
+		uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", fmt.Sprintf("/run/user/%d/podman/podman.sock", m.UID), strconv.Itoa(m.Port), m.RemoteUsername)
 		uriRoot := machine.SSHRemoteConnection.MakeSSHURL("localhost", "/run/podman/podman.sock", strconv.Itoa(m.Port), "root")
 		identity := m.IdentityPath
 
@@ -252,7 +280,9 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
 		Name:     "ready.service",
 		Contents: machine.StrToPtr(fmt.Sprintf(ready, "vsock")),
 	}
+	virtiofsUnits := generateSystemDFilesForVirtiofsMounts(virtiofsMnts)
 	ign.Cfg.Systemd.Units = append(ign.Cfg.Systemd.Units, readyUnit)
+	ign.Cfg.Systemd.Units = append(ign.Cfg.Systemd.Units, virtiofsUnits...)
 
 	if err := ign.Write(); err != nil {
 		return false, err
@@ -418,9 +448,7 @@ func (m *MacMachine) SSH(name string, opts machine.SSHOptions) error {
 	if username == "" {
 		username = m.RemoteUsername
 	}
-	// TODO when host networking is figured out, we need to switch this back to
-	// machine.commonssh
-	return AppleHVSSH(username, m.IdentityPath, m.Name, m.Port, opts.Args)
+	return machine.CommonSSH(username, m.IdentityPath, m.Name, m.Port, opts.Args)
 }
 
 func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
@@ -441,22 +469,23 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
 	defer ioEater.Close()
 
 	// TODO handle returns from startHostNetworking
-	_, _, err = m.startHostNetworking(ioEater)
+	forwardSock, forwardState, err := m.startHostNetworking(ioEater)
 	if err != nil {
 		return err
 	}
 
 	// Add networking
-	// TODO this creates a nat'd connection and we still need to switch over
-	// to use gvproxy and random ssh ports.
 	netDevice, err := vfConfig.VirtioNetNew("5a:94:ef:e4:0c:ee")
 	if err != nil {
 		return err
 	}
+	// Set user networking with gvproxy
+	netDevice.SetUnixSocketPath(m.GvProxySock.GetPath())
+
 	m.Vfkit.VirtualMachine.Devices = append(m.Vfkit.VirtualMachine.Devices, netDevice)
 
 	for _, vol := range m.Mounts {
-		virtfsDevice, err := vfConfig.VirtioFsNew(vol.Source, "podmanHomeDir")
+		virtfsDevice, err := vfConfig.VirtioFsNew(vol.Source, vol.Tag)
 		if err != nil {
 			return err
 		}
@@ -464,7 +493,6 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
 	}
 
 	// To start the VM, we need to call vfkit
-	// TODO need to hold the start command until fcos tells us it is started
 
 	cmd, err := m.Vfkit.VirtualMachine.Cmd(m.Vfkit.VfkitBinaryPath.Path)
 	if err != nil {
@@ -480,6 +508,7 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
 		return err
 	}
 	cmd.Args = append(cmd.Args, restArgs...)
+
 	firstBoot, err := m.isFirstBoot()
 	if err != nil {
 		return err
@@ -522,15 +551,11 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
 	cmd.ExtraFiles = []*os.File{ioEater, ioEater, ioEater}
 	fmt.Println(cmd.Args)
 
-	readSocketBaseDir := filepath.Base(m.ReadySocket.GetPath())
+	readSocketBaseDir := filepath.Dir(m.ReadySocket.GetPath())
 	if err := os.MkdirAll(readSocketBaseDir, 0755); err != nil {
 		return err
 	}
 
-	if err := cmd.Start(); err != nil {
-		return err
-	}
-
 	if firstBoot {
 		logrus.Debug("first boot detected")
 		logrus.Debugf("serving ignition file over %s", ignitionSocket.GetPath())
@@ -550,11 +575,21 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
 		return err
 	}
 	logrus.Debug("waiting for ready notification")
-	conn, err := readyListen.Accept()
-	if err != nil {
+	var conn net.Conn
+	readyChan := make(chan error)
+	go func() {
+		conn, err = readyListen.Accept()
+		if err != nil {
+			logrus.Error(err)
+		}
+		_, err = bufio.NewReader(conn).ReadString('\n')
+		readyChan <- err
+	}()
+	if err := cmd.Start(); err != nil {
 		return err
 	}
-	_, err = bufio.NewReader(conn).ReadString('\n')
+
+	err = <-readyChan
 	defer func() {
 		if closeErr := conn.Close(); closeErr != nil {
 			logrus.Error(closeErr)
@@ -564,6 +599,7 @@ func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
 		return err
 	}
 	logrus.Debug("ready notification received")
+	m.waitAPIAndPrintInfo(forwardState, forwardSock, opts.NoInfo)
 	return nil
 }
 
@@ -583,6 +619,34 @@ func (m *MacMachine) Stop(name string, opts machine.StopOptions) error {
 	if vmState != machine.Running {
 		return machine.ErrWrongState
 	}
+
+	defer func() {
+		// I think "soft" errors here is OK
+		gvPid, err := m.GvProxyPid.Read()
+		if err != nil {
+			logrus.Error(fmt.Errorf("unable to read gvproxy pid file %s: %v", m.GvProxyPid.GetPath(), err))
+			return
+		}
+		proxyPid, err := strconv.Atoi(string(gvPid))
+		if err != nil {
+			logrus.Error(fmt.Errorf("unable to convert pid to integer: %v", err))
+			return
+		}
+		proxyProc, err := os.FindProcess(proxyPid)
+		if proxyProc == nil && err != nil {
+			logrus.Error("unable to find process: %v", err)
+			return
+		}
+		if err := proxyProc.Kill(); err != nil {
+			logrus.Error("unable to kill gvproxy: %v", err)
+			return
+		}
+		// gvproxy does not clean up its pid file on exit
+		if err := m.GvProxyPid.Delete(); err != nil {
+			logrus.Error("unable to delete gvproxy pid file: %v", err)
+		}
+	}()
+
 	return m.Vfkit.stop(false, true)
 }
 
@@ -699,11 +763,35 @@ func (m *MacMachine) startHostNetworking(ioEater *os.File) (string, machine.APIF
 		forwardSock string
 		state       machine.APIForwardingState
 	)
+
+	// TODO This should probably be added to startHostNetworking everywhere
+	// GvProxy does not clean up after itself
+	if err := m.GvProxySock.Delete(); err != nil {
+		b, err := m.GvProxyPid.Read()
+		if err != nil {
+			return "", machine.NoForwarding, err
+		}
+		pid, err := strconv.Atoi(string(b))
+		if err != nil {
+			return "", 0, err
+		}
+		gvProcess, err := os.FindProcess(pid)
+		if err != nil {
+			return "", 0, err
+		}
+		// shoot it with a signal 0 and see if it is active
+		err = gvProcess.Signal(syscall.Signal(0))
+		if err == nil {
+			return "", 0, fmt.Errorf("gvproxy process %s already running", string(b))
+		}
+		if err := m.GvProxySock.Delete(); err != nil {
+			return "", 0, err
+		}
+	}
 	cfg, err := config.Default()
 	if err != nil {
 		return "", machine.NoForwarding, err
 	}
-
 	attr := new(os.ProcAttr)
 	gvproxy, err := cfg.FindHelperBinary("gvproxy", false)
 	if err != nil {
@@ -714,12 +802,16 @@ func (m *MacMachine) startHostNetworking(ioEater *os.File) (string, machine.APIF
 	cmd := []string{gvproxy}
 	// Add the ssh port
 	cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", m.Port)}...)
-
+	// Add pid file
+	cmd = append(cmd, "-pid-file", m.GvProxyPid.GetPath())
+	// Add vfkit proxy listen
+	cmd = append(cmd, "-listen-vfkit", fmt.Sprintf("unixgram://%s", m.GvProxySock.GetPath()))
 	cmd, forwardSock, state = m.setupAPIForwarding(cmd)
 	if logrus.GetLevel() == logrus.DebugLevel {
 		cmd = append(cmd, "--debug")
 		fmt.Println(cmd)
 	}
+
 	_, err = os.StartProcess(cmd[0], cmd, attr)
 	if err != nil {
 		return "", 0, fmt.Errorf("unable to execute: %q: %w", cmd, err)
@@ -770,6 +862,42 @@ func (m *MacMachine) setupAPIForwarding(cmd []string) ([]string, string, machine
 	cmd = append(cmd, []string{"-forward-user", forwardUser}...)
 	cmd = append(cmd, []string{"-forward-identity", m.IdentityPath}...)
 
+	link, err := m.userGlobalSocketLink()
+	if err != nil {
+		return cmd, socket.GetPath(), machine.MachineLocal
+	}
+
+	if !dockerClaimSupported() {
+		return cmd, socket.GetPath(), machine.ClaimUnsupported
+	}
+
+	if !dockerClaimHelperInstalled() {
+		return cmd, socket.GetPath(), machine.NotInstalled
+	}
+
+	if !alreadyLinked(socket.GetPath(), link) {
+		if checkSockInUse(link) {
+			return cmd, socket.GetPath(), machine.MachineLocal
+		}
+
+		_ = os.Remove(link)
+		if err = os.Symlink(socket.GetPath(), link); err != nil {
+			logrus.Warnf("could not create user global API forwarding link: %s", err.Error())
+			return cmd, socket.GetPath(), machine.MachineLocal
+		}
+	}
+
+	if !alreadyLinked(link, dockerSock) {
+		if checkSockInUse(dockerSock) {
+			return cmd, socket.GetPath(), machine.MachineLocal
+		}
+
+		if !claimDockerSock() {
+			logrus.Warn("podman helper is installed, but was not able to claim the global docker sock")
+			return cmd, socket.GetPath(), machine.MachineLocal
+		}
+	}
+
 	return cmd, "", machine.MachineLocal
 }
 
@@ -815,13 +943,131 @@ func (m *MacMachine) getIgnitionSock() (*machine.VMFile, error) {
 	if err != nil {
 		return nil, err
 	}
+	if err := os.MkdirAll(dataDir, 0755); err != nil {
+		if !errors.Is(err, os.ErrExist) {
+			return nil, err
+		}
+	}
 	return machine.NewMachineFile(filepath.Join(dataDir, ignitionSocketName), nil)
 }
 
-func getRuntimeDir() (string, error) {
+func (m *MacMachine) getRuntimeDir() (string, error) {
 	tmpDir, ok := os.LookupEnv("TMPDIR")
 	if !ok {
 		tmpDir = "/tmp"
 	}
-	return tmpDir, nil
+	return filepath.Join(tmpDir, "podman"), nil
+}
+
+func (m *MacMachine) userGlobalSocketLink() (string, error) {
+	path, err := machine.GetDataDir(machine.AppleHvVirt)
+	if err != nil {
+		logrus.Errorf("Resolving data dir: %s", err.Error())
+		return "", err
+	}
+	// User global socket is located in parent directory of machine dirs (one per user)
+	return filepath.Join(filepath.Dir(path), "podman.sock"), err
+}
+
+func (m *MacMachine) waitAPIAndPrintInfo(forwardState machine.APIForwardingState, forwardSock string, noInfo bool) {
+	suffix := ""
+	if m.Name != machine.DefaultMachineName {
+		suffix = " " + m.Name
+	}
+
+	if m.isIncompatible() {
+		fmt.Fprintf(os.Stderr, "\n!!! ACTION REQUIRED: INCOMPATIBLE MACHINE !!!\n")
+
+		fmt.Fprintf(os.Stderr, "\nThis machine was created by an older Podman release that is incompatible\n")
+		fmt.Fprintf(os.Stderr, "with this release of Podman. It has been started in a limited operational\n")
+		fmt.Fprintf(os.Stderr, "mode to allow you to copy any necessary files before recreating it. This\n")
+		fmt.Fprintf(os.Stderr, "can be accomplished with the following commands:\n\n")
+		fmt.Fprintf(os.Stderr, "\t# Login and copy desired files (Optional)\n")
+		fmt.Fprintf(os.Stderr, "\t# Podman machine ssh%s tar cvPf - /path/to/files > backup.tar\n\n", suffix)
+		fmt.Fprintf(os.Stderr, "\t# Recreate machine (DESTRUCTIVE!) \n")
+		fmt.Fprintf(os.Stderr, "\tpodman machine stop%s\n", suffix)
+		fmt.Fprintf(os.Stderr, "\tpodman machine rm -f%s\n", suffix)
+		fmt.Fprintf(os.Stderr, "\tpodman machine init --now%s\n\n", suffix)
+		fmt.Fprintf(os.Stderr, "\t# Copy back files (Optional)\n")
+		fmt.Fprintf(os.Stderr, "\t# cat backup.tar | podman machine ssh%s tar xvPf - \n\n", suffix)
+	}
+
+	if forwardState == machine.NoForwarding {
+		return
+	}
+
+	machine.WaitAndPingAPI(forwardSock)
+
+	if !noInfo {
+		if !m.Rootful {
+			fmt.Printf("\nThis machine is currently configured in rootless mode. If your containers\n")
+			fmt.Printf("require root permissions (e.g. ports < 1024), or if you run into compatibility\n")
+			fmt.Printf("issues with non-Podman clients, you can switch using the following command: \n")
+			fmt.Printf("\n\tpodman machine set --rootful%s\n\n", suffix)
+		}
+
+		fmt.Printf("API forwarding listening on: %s\n", forwardSock)
+		if forwardState == machine.DockerGlobal {
+			fmt.Printf("Docker API clients default to this address. You do not need to set DOCKER_HOST.\n\n")
+		} else {
+			stillString := "still "
+			switch forwardState {
+			case machine.NotInstalled:
+				fmt.Printf("\nThe system helper service is not installed; the default Docker API socket\n")
+				fmt.Printf("address can't be used by Podman. ")
+				if helper := findClaimHelper(); len(helper) > 0 {
+					fmt.Printf("If you would like to install it run the\nfollowing commands:\n")
+					fmt.Printf("\n\tsudo %s install\n", helper)
+					fmt.Printf("\tpodman machine stop%s; podman machine start%s\n\n", suffix, suffix)
+				}
+			case machine.MachineLocal:
+				fmt.Printf("\nAnother process was listening on the default Docker API socket address.\n")
+			case machine.ClaimUnsupported:
+				fallthrough
+			default:
+				stillString = ""
+			}
+
+			fmt.Printf("You can %sconnect Docker API clients by setting DOCKER_HOST using the\n", stillString)
+			fmt.Printf("following command in your terminal session:\n")
+			fmt.Printf("\n\texport DOCKER_HOST='unix://%s'\n\n", forwardSock)
+		}
+	}
+}
+
+func (m *MacMachine) isIncompatible() bool {
+	return m.UID == -1
+}
+
+func generateSystemDFilesForVirtiofsMounts(mounts []machine.VirtIoFs) []machine.Unit {
+	var unitFiles []machine.Unit
+
+	for _, mnt := range mounts {
+		autoMountUnit := `[Automount]
+Where=%s
+[Install]
+WantedBy=multi-user.target
+[Unit]
+Description=Mount virtiofs volume %s
+`
+		mountUnit := `[Mount]
+What=%s
+Where=%s
+Type=virtiofs
+[Install]
+WantedBy=multi-user.target`
+
+		virtiofsAutomount := machine.Unit{
+			Enabled:  machine.BoolToPtr(true),
+			Name:     fmt.Sprintf("%s.automount", mnt.Tag),
+			Contents: machine.StrToPtr(fmt.Sprintf(autoMountUnit, mnt.Target, mnt.Target)),
+		}
+		virtiofsMount := machine.Unit{
+			Enabled:  machine.BoolToPtr(true),
+			Name:     fmt.Sprintf("%s.mount", mnt.Tag),
+			Contents: machine.StrToPtr(fmt.Sprintf(mountUnit, mnt.Tag, mnt.Target)),
+		}
+		unitFiles = append(unitFiles, virtiofsAutomount, virtiofsMount)
+	}
+	return unitFiles
 }
diff --git a/pkg/machine/applehv/vfkit.go b/pkg/machine/applehv/vfkit.go
index 24cd22c918..99279210dd 100644
--- a/pkg/machine/applehv/vfkit.go
+++ b/pkg/machine/applehv/vfkit.go
@@ -4,6 +4,7 @@
 package applehv
 
 import (
+	"github.com/containers/podman/v4/pkg/machine"
 	vfConfig "github.com/crc-org/vfkit/pkg/config"
 )
 
@@ -52,3 +53,10 @@ func getDebugDevices() ([]vfConfig.VirtioDevice, error) {
 func getIgnitionVsockDevice(path string) (vfConfig.VirtioDevice, error) {
 	return vfConfig.VirtioVsockNew(1024, path, true)
 }
+
+func VirtIOFsToVFKitVirtIODevice(fs machine.VirtIoFs) vfConfig.VirtioFs {
+	return vfConfig.VirtioFs{
+		SharedDir: fs.Source,
+		MountTag:  fs.Tag,
+	}
+}
diff --git a/pkg/machine/config.go b/pkg/machine/config.go
index 8b915321a7..ebcb1495ca 100644
--- a/pkg/machine/config.go
+++ b/pkg/machine/config.go
@@ -4,9 +4,11 @@
 package machine
 
 import (
+	"context"
 	"errors"
 	"fmt"
 	"net"
+	"net/http"
 	"net/url"
 	"os"
 	"path/filepath"
@@ -46,6 +48,7 @@ const (
 	// Starting indicated the vm is in the process of starting
 	Starting           Status = "starting"
 	DefaultMachineName string = "podman-machine-default"
+	apiUpTimeout              = 20 * time.Second
 )
 
 type RemoteConnectionType string
@@ -454,3 +457,28 @@ func NewVirtualization(artifact Artifact, compression ImageCompression, format I
 		format,
 	}
 }
+
+func WaitAndPingAPI(sock string) {
+	client := http.Client{
+		Transport: &http.Transport{
+			DialContext: func(context.Context, string, string) (net.Conn, error) {
+				con, err := net.DialTimeout("unix", sock, apiUpTimeout)
+				if err != nil {
+					return nil, err
+				}
+				if err := con.SetDeadline(time.Now().Add(apiUpTimeout)); err != nil {
+					return nil, err
+				}
+				return con, nil
+			},
+		},
+	}
+
+	resp, err := client.Get("http://host/_ping")
+	if err == nil {
+		defer resp.Body.Close()
+	}
+	if err != nil || resp.StatusCode != 200 {
+		logrus.Warn("API socket failed ping test")
+	}
+}
diff --git a/pkg/machine/machine_unix.go b/pkg/machine/machine_unix.go
new file mode 100644
index 0000000000..335da02494
--- /dev/null
+++ b/pkg/machine/machine_unix.go
@@ -0,0 +1,35 @@
+//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd
+
+package machine
+
+import (
+	"errors"
+	"strings"
+)
+
+// ParseVolumeFromPath is a oneshot parsing of a provided volume.  It follows the "rules" of
+// the singular parsing functions
+func ParseVolumeFromPath(v string) (source, target, options string, readonly bool, err error) {
+	split := strings.SplitN(v, ":", 3)
+	switch len(split) {
+	case 1:
+		source = split[0]
+		target = split[0]
+	case 2:
+		source = split[0]
+		target = split[1]
+	case 3:
+		source = split[0]
+		target = split[1]
+		options = split[2]
+	default:
+		return "", "", "", false, errors.New("invalid volume provided")
+	}
+
+	// I suppose an option not intended for read-only could interfere here but I do not see a better way
+	if strings.Contains(options, "ro") {
+		readonly = true
+	}
+	return
+}
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index 783d917b68..53cfe6fc17 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -6,14 +6,12 @@ package qemu
 import (
 	"bufio"
 	"bytes"
-	"context"
 	"encoding/base64"
 	"encoding/json"
 	"errors"
 	"fmt"
 	"io/fs"
 	"net"
-	"net/http"
 	"net/url"
 	"os"
 	"os/exec"
@@ -44,7 +42,6 @@ const (
 	MountType9p          = "9p"
 	dockerSock           = "/var/run/docker.sock"
 	dockerConnectTimeout = 5 * time.Second
-	apiUpTimeout         = 20 * time.Second
 )
 
 type MachineVM struct {
@@ -1287,31 +1284,6 @@ func alreadyLinked(target string, link string) bool {
 	return err == nil && read == target
 }
 
-func waitAndPingAPI(sock string) {
-	client := http.Client{
-		Transport: &http.Transport{
-			DialContext: func(context.Context, string, string) (net.Conn, error) {
-				con, err := net.DialTimeout("unix", sock, apiUpTimeout)
-				if err != nil {
-					return nil, err
-				}
-				if err := con.SetDeadline(time.Now().Add(apiUpTimeout)); err != nil {
-					return nil, err
-				}
-				return con, nil
-			},
-		},
-	}
-
-	resp, err := client.Get("http://host/_ping")
-	if err == nil {
-		defer resp.Body.Close()
-	}
-	if err != nil || resp.StatusCode != 200 {
-		logrus.Warn("API socket failed ping test")
-	}
-}
-
 func (v *MachineVM) waitAPIAndPrintInfo(forwardState machine.APIForwardingState, forwardSock string, noInfo bool) {
 	suffix := ""
 	if v.Name != machine.DefaultMachineName {
@@ -1339,7 +1311,7 @@ func (v *MachineVM) waitAPIAndPrintInfo(forwardState machine.APIForwardingState,
 		return
 	}
 
-	waitAndPingAPI(forwardSock)
+	machine.WaitAndPingAPI(forwardSock)
 
 	if !noInfo {
 		if !v.Rootful {
diff --git a/pkg/machine/volumes.go b/pkg/machine/volumes.go
new file mode 100644
index 0000000000..5f7eaf07de
--- /dev/null
+++ b/pkg/machine/volumes.go
@@ -0,0 +1,61 @@
+package machine
+
+import (
+	"strings"
+)
+
+type Volume interface {
+	Kind() VolumeKind
+}
+
+type VolumeKind string
+
+var (
+	VirtIOFsVk VolumeKind = "virtiofs"
+	NinePVk    VolumeKind = "9p"
+)
+
+type VirtIoFs struct {
+	VolumeKind
+	ReadOnly bool
+	Source   string
+	Tag      string
+	Target   string
+}
+
+func (v VirtIoFs) Kind() string {
+	return string(VirtIOFsVk)
+}
+
+// unitName is the fq path where /'s are replaced with -'s
+func (v VirtIoFs) unitName() string {
+	// delete the leading -
+	unit := strings.ReplaceAll(v.Target, "/", "-")
+	if strings.HasPrefix(unit, "-") {
+		return unit[1:]
+	}
+	return unit
+}
+
+func (v VirtIoFs) ToMount() Mount {
+	return Mount{
+		ReadOnly: v.ReadOnly,
+		Source:   v.Source,
+		Tag:      v.Tag,
+		Target:   v.Target,
+		Type:     v.Kind(),
+	}
+}
+
+// NewVirtIoFsMount describes a machine volume mount for virtio-fs.  With virtio-fs
+// the source/target are described as a "shared dir".  With this style of volume mount
+// the Tag is used as the descriptor value for the mount (in Linux).
+func NewVirtIoFsMount(src, target string, readOnly bool) VirtIoFs {
+	vfs := VirtIoFs{
+		ReadOnly: readOnly,
+		Source:   src,
+		Target:   target,
+	}
+	vfs.Tag = vfs.unitName()
+	return vfs
+}