diff --git a/cmd/hepto/config.go b/cmd/hepto/config.go
index 7eaf8007fe0ab3ce72baba39ccd1d2658dd08cb2..3eb32fdfb1ea34693c0f15b733b8996f125ce750 100644
--- a/cmd/hepto/config.go
+++ b/cmd/hepto/config.go
@@ -62,7 +62,11 @@ func (c *Config) FlagSet() *flag.FlagSet {
 	fs.IntVar(&config.Node.Port, "discovery-port", 7123, "TCP port used for discovery")
 	fs.StringVar(&config.Node.Name, "name", "", "Hepto node name")
 	fs.StringVar(&config.Node.Role, "role", "node", "Node role inside the cluster")
-	// TODO anchors
+	fs.Func("anchor", "IP address or name of an another cluster node", func(raw string) error {
+		ip, err := netip.ParseAddr(raw)
+		c.Node.Anchors = append(c.Node.Anchors, ip.String())
+		return err
+	})
 	return fs
 }
 
diff --git a/cmd/hepto/hepto.go b/cmd/hepto/hepto.go
index 34630876b5b27c84623e49c61e5f53578a16748f..999b9e32d9da5d32460fb61aff963db3aff1f661 100644
--- a/cmd/hepto/hepto.go
+++ b/cmd/hepto/hepto.go
@@ -1,11 +1,12 @@
 package hepto
 
 import (
+	"flag"
 	"fmt"
-	"net"
+	"net/netip"
 	"os"
 	"path"
-	"time"
+	"strings"
 
 	"go.acides.org/dolly"
 	"go.acides.org/hepto/services"
@@ -13,9 +14,15 @@ import (
 
 // The main hepto command
 func Hepto() error {
-	// First parse flags and validate the configuration
+	// First parse flags, load enviromnemt variables, and validate the configuration
 	fs := config.FlagSet()
 	err := fs.Parse(os.Args[1:])
+	fs.VisitAll(func(f *flag.Flag) {
+		val, ok := os.LookupEnv(fmt.Sprintf("HEPTO_%s", strings.ToUpper(f.Name)))
+		if ok {
+			f.Value.Set(val)
+		}
+	})
 	if err == nil {
 		err = config.Validate()
 	}
@@ -26,87 +33,30 @@ func Hepto() error {
 	// Create the container and run the actual service
 	dataPath := path.Join(config.DataDir, config.Cluster.Name, config.Node.Name)
 	config.Cluster.DataDir = "/data"
-	self, err := os.Executable()
-	if err != nil {
-		return err
-	}
+	config.Iface.ReadinessProbe = netip.AddrPortFrom(config.Network.DNS[0], 53)
 	c := dolly.NewForking()
 	c.AddAll(
-		dolly.NewPid(),
+		dolly.NewContainer(config.Node.Name),
 		&config.Network,
-		&dolly.Certs{},
 		&config.Iface,
 		dolly.NewSystem(desiredModules, systemSettings),
-		dolly.NewEmptyRoot(config.Node.Name),
-		dolly.DefaultVolumes(),
 		dolly.NewBind(dataPath, config.Cluster.DataDir),
-		dolly.NewBind(self, self),
+		dolly.MountSelf("/bin", "runc", "mount", "umount"),
 		dolly.NewDevicesOrPanic(requiredDevices...),
 		dolly.NewBinds(config.Mounts),
 	)
 	if config.Node.Role == "master" || config.Node.Role == "full" {
 		c.Add(&dolly.Forward{
-			HostBind:      net.ParseIP("::1"),
-			HostPort:      config.Cluster.LoopbackPort,
-			ContainerIP:   net.ParseIP("::1"),
-			ContainerPort: 6443,
+			From: netip.AddrPortFrom(netip.MustParseAddr("::1"), uint16(config.Cluster.LoopbackPort)),
+			To:   netip.AddrPortFrom(netip.MustParseAddr("::1"), 6443),
 		})
 	}
-	return c.Run(hepto)
-}
-
-// The actuall hepto code, run inside a container by the command
-func hepto() error {
-	// Install ourselves as a hooking binary
-	self, err := os.Executable()
-	if err != nil {
-		return fmt.Errorf("could not get executable path: %w", err)
-	}
-	err = os.Setenv("PATH", "/bin")
-	if err != nil {
-		return fmt.Errorf("could not set binaries path: %w", err)
-	}
-	err = os.MkdirAll("/bin", 0o755)
-	if err != nil {
-		return fmt.Errorf("could not prepare bin dir: %w", err)
-	}
-	for cmd := range Hooks {
-		err = os.Symlink(self, path.Join("/bin", cmd))
-		if err != nil {
-			return fmt.Errorf("could not install binary %s: %w", cmd, err)
-		}
-	}
-	// Temporary directories are not created by selfcontain, but / is a tmpfs, so just mkdir
-	for _, dir := range []string{"/tmp", "/run"} {
-		err = os.Mkdir(dir, 0o777)
+	return c.Run(func() error {
+		config.Node.IP = config.Iface.Addresses[0].Addr().AsSlice()
+		cluster, err := services.NewManager(&config.Cluster, &config.Node, config.Logger)
 		if err != nil {
 			return err
 		}
-	}
-	// Otherwisem rely on opening a socket and probing the socket local
-	// address for more reliability
-	config.Logger.Info("determining current IP address")
-	for {
-		time.Sleep(time.Second)
-		target := fmt.Sprintf("[%s]:53", config.Network.DNS[0].String())
-		config.Logger.Info("connecting outbound to guess IP", "target", target)
-		conn, err := net.Dial("udp", target)
-		if err != nil {
-			config.Logger.Info("could not connect")
-			continue
-		}
-		localAddr := conn.LocalAddr().(*net.UDPAddr).IP
-		conn.Close()
-		if localAddr.IsLoopback() || localAddr.IsLinkLocalUnicast() {
-			continue
-		}
-		config.Logger.Info("found current IP", "ip", config.Node.IP.String())
-		config.Node.IP = localAddr
-		break
-	}
-	c, err := services.NewManager(&config.Cluster, &config.Node, config.Logger)
-	if err != nil {
-		return err
-	}
-	return c.Run()
+		return cluster.Run()
+	})
 }
diff --git a/docs/example.env b/docs/example.env
new file mode 100644
index 0000000000000000000000000000000000000000..621a723c7c864ba3c3ec398c7b9cdae2fa2b283b
--- /dev/null
+++ b/docs/example.env
@@ -0,0 +1,2 @@
+export HEPTO_CLUSTER=hepto
+export HEPTO_KEY=deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
\ No newline at end of file
diff --git a/go.mod b/go.mod
index d2e4e0e791a8c3672a83f48f75186113996d467c..570efe9c835fc90df9ea60b4d90dc9cfa306808f 100644
--- a/go.mod
+++ b/go.mod
@@ -60,7 +60,7 @@ require (
 	github.com/sirupsen/logrus v1.9.3
 	github.com/vishvananda/netlink v1.2.1-beta.2
 	go.acides.org/daeman v0.3.4
-	go.acides.org/dolly v0.4.0
+	go.acides.org/dolly v0.4.2
 	go.acides.org/pekahi v0.2.1
 	go.acides.org/sml v0.2.2
 	go.etcd.io/etcd/server/v3 v3.5.9
diff --git a/go.sum b/go.sum
index 00d874e287a3dd111625a8aef9cf350c7bcd86a0..0b56f01d05a20b5cd3bf227c031a36bd30797b8d 100644
--- a/go.sum
+++ b/go.sum
@@ -1142,8 +1142,8 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS
 github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
 go.acides.org/daeman v0.3.4 h1:gjKq95TpP2+IMUkW/u1do4J43Y6KhTOPwOg6BBCBT74=
 go.acides.org/daeman v0.3.4/go.mod h1:fOZ4YudsVjbUan8WYTs0ED6JfpZ7Z6mlN4Lnn1qW5nk=
-go.acides.org/dolly v0.4.0 h1:wy2SDuyAZU99eNjbMERkEb4KDUxh4yXR5zTgrzQUAkM=
-go.acides.org/dolly v0.4.0/go.mod h1:eO56y+YmSx2h0/Zy7+p2zxNeJVl4iIWMM/vsRFMaIDM=
+go.acides.org/dolly v0.4.2 h1:PXlNJrG4fL/OYjllvWr3MPRKe5L1sSetexNtaiWyaqw=
+go.acides.org/dolly v0.4.2/go.mod h1:eO56y+YmSx2h0/Zy7+p2zxNeJVl4iIWMM/vsRFMaIDM=
 go.acides.org/pekahi v0.2.1 h1:ysmC14q+hnYXcuB1Ew2XoWDZsgR1jbut89ACRLwi43I=
 go.acides.org/pekahi v0.2.1/go.mod h1:AxgN7Ss6dCRHoNOVWMymkmDafWYdDV7ce6jPl5bqyRc=
 go.acides.org/sml v0.2.2 h1:swwqh/MoYO/7AifuIefhfw2XeVEyXU46Uz+VtDWiDNE=