diff --git a/dospin.go b/dospin.go
index 81cd02c..ca6335d 100644
--- a/dospin.go
+++ b/dospin.go
@@ -18,14 +18,16 @@ const (
 )
 
 type cmdOptions struct {
-	config string
-	cmd    string
+	config      string
+	cmd         string
+	varStateDir string
 }
 
 func parseCmdOptions() cmdOptions {
 	var o cmdOptions
 	flag.StringVar(&o.cmd, "cmd", CMD_SERVE, "Mode to run command in ("+CMD_SERVE+","+CMD_SPINDOWNALL+")")
-	flag.StringVar(&o.config, "config", "dospin.json", "Path to the dospin config file")
+	flag.StringVar(&o.config, "config", "/etc/dospin.json", "Path to the dospin config file")
+	flag.StringVar(&o.varStateDir, "varstate", "/var/lib/dospin", "Path to the var state directory")
 	flag.Parse()
 	return o
 }
diff --git a/dospin.json b/dospin.json
index 5d394f4..82399f2 100644
--- a/dospin.json
+++ b/dospin.json
@@ -1,12 +1,17 @@
 {
 	"ApiToken": "<your token here>",
+	"GatewayInterface": "eth0:2",
 	"Servers": {
 		"minecraft": {
 			"Ports": [25565],
 			"UsePublicIP": false,
-			"InitialSize": "512mb",
+			"InitialSize": "4gb",
 			"Size": "4gb",
-			"Region": "nyc3"
+			"Region": "nyc1",
+			"SshKeys": [513314, 1667247],
+			"ImageSlug": "ubuntu-16-04-x64",
+			"Volumes": ["volume-nyc1-01"],
+			"UserData": "#!/bin/bash\napt-get update\napt-get install -y docker.io\nmkdir -p /mnt/volume-nyc1-01\nmount -o discard,defaults /dev/disk/by-id/scsi-0DO_Volume_volume-nyc1-01 /mnt/volume-nyc1-01\necho /dev/disk/by-id/scsi-0DO_Volume_volume-nyc1-01 /mnt/volume-nyc1-01 ext4 defaults,nofail,discard 0 0 | tee -a /etc/fstab\ndocker run -d --restart=always -p 25565:25565 -v /mnt/volume-nyc1-01/minecraft-server:/minecraft-server -w /minecraft-server -t java:8-alpine sh start.sh"
 		}
 	}
 }
diff --git a/droplethandler.go b/droplethandler.go
index c79be8c..9423865 100644
--- a/droplethandler.go
+++ b/droplethandler.go
@@ -28,6 +28,22 @@ func (t *tokenSource) Token() (*oauth2.Token, error) {
 	return token, nil
 }
 
+func sshKeys(ids []int) []godo.DropletCreateSSHKey {
+	var out []godo.DropletCreateSSHKey
+	for _, id := range ids {
+		out = append(out, godo.DropletCreateSSHKey{ID: id})
+	}
+	return out
+}
+
+func volumes(names []string) []godo.DropletCreateVolume {
+	var out []godo.DropletCreateVolume
+	for _, name := range names {
+		out = append(out, godo.DropletCreateVolume{Name: name})
+	}
+	return out
+}
+
 type DropletHandler struct {
 	client   *godo.Client
 	settings Settings
@@ -58,9 +74,20 @@ func (me *DropletHandler) Spinup(name string) (string, error) {
 		}
 	} else {
 		// create the droplet
-		image, err := me.getSnapshot(name)
-		if err != nil {
-			return "", err
+		var image godo.DropletCreateImage
+
+		if vd.ImageSlug == "" {
+			snapshot, err := me.getSnapshot(name)
+			if err != nil {
+				return "", err
+			}
+			image = godo.DropletCreateImage{
+				ID: snapshot.ID,
+			}
+		} else {
+			image = godo.DropletCreateImage{
+				Slug: vd.ImageSlug,
+			}
 		}
 
 		// determine droplet size
@@ -76,9 +103,10 @@ func (me *DropletHandler) Spinup(name string) (string, error) {
 			Region:            vd.Region,
 			Size:              size,
 			PrivateNetworking: true,
-			Image: godo.DropletCreateImage{
-				ID: image.ID,
-			},
+			SSHKeys:           sshKeys(vd.SshKeys),
+			Volumes:           volumes(vd.Volumes),
+			UserData:          vd.UserData,
+			Image:             image,
 		}
 
 		log.Println("Spinup: Creating " + name)
@@ -129,14 +157,22 @@ func (me *DropletHandler) Spinup(name string) (string, error) {
 		_, err = me.client.Images.Delete(image.ID)
 		if err != nil {
 			log.Println("Spinup: Could not delete image: ", err)
+		} else {
+			log.Println("Spinup: Deleted image " + name)
 		}
-		log.Println("Spinup: Deleted image " + name)
 
 		// get the private IP and return it
-		if vd.UsePublicIP {
-			return droplet.PublicIPv4()
+
+		// get new copy of droplet that has IP
+		droplet, _, err = me.client.Droplets.Get(droplet.ID)
+		if err == nil {
+			if vd.UsePublicIP {
+				return droplet.PublicIPv4()
+			} else {
+				return droplet.PrivateIPv4()
+			}
 		} else {
-			return droplet.PrivateIPv4()
+			return "", err
 		}
 	}
 }
diff --git a/list_keys.sh b/list_keys.sh
new file mode 100755
index 0000000..1bad52c
--- /dev/null
+++ b/list_keys.sh
@@ -0,0 +1,4 @@
+#! /usr/bin/env sh
+
+echo Keys
+curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer `cat dospin.json | jq -r .ApiToken`" "https://api.digitalocean.com/v2/account/keys" | jq .
diff --git a/list_volumes.sh b/list_volumes.sh
new file mode 100755
index 0000000..f09d88e
--- /dev/null
+++ b/list_volumes.sh
@@ -0,0 +1,4 @@
+#! /usr/bin/env sh
+
+echo Volumes
+curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer `cat dospin.json | jq -r .ApiToken`" "https://api.digitalocean.com/v2/volumes" | jq .
diff --git a/net_linux.go b/net_linux.go
index bf62b28..bb53ca3 100644
--- a/net_linux.go
+++ b/net_linux.go
@@ -8,29 +8,27 @@
 package main
 
 import (
-	"bytes"
 	"log"
 	"os/exec"
-	"strconv"
 )
 
-// just have this stub to allow building on Linux
-func addPortForward(ruleName, ip, port string) {
-	log.Print("Port forwarding not currently implemented for Linux/iptables")
+func addPortForward(ruleName, gatewayInt, localIp, targetIp, port string) {
+	log.Println("Setting up port", port, "to", targetIp)
+	cmdOut, err := exec.Command("iptables", "-A", "PREROUTING", "-t", "nat", "-i", "\""+gatewayInt+"\"", "-p", "tcp", "--dport", port, "-j", "DNAT", "--to", targetIp+":"+port).Output()
+	log.Println("iptables", "-A", "PREROUTING,", "-t", "nat", "-i", "\""+gatewayInt+"\"", "-p", "tcp", "--dport", port, "-j", "DNAT", "--to", targetIp+":"+port)
+	if err != nil {
+		log.Println("iptables error:", err)
+	}
+	log.Println(cmdOut)
+
+	cmdOut, err = exec.Command("iptables", "-A", "FORWARD", "-p", "tcp", "-d", targetIp, "--dport", port, "-j", "ACCEPT").Output()
+	log.Println("iptables", "-A", "FORWARD", "-p", "tcp", "-d", targetIp, "--dport", port, "-j", "ACCEPT")
+	if err != nil {
+		log.Println("iptables error:", err)
+	}
+	log.Println(cmdOut)
 }
 
 func rmPortForward(ruleName string) {
 	log.Print("Port forwarding not currently implemented for Linux/iptables")
 }
-
-func portUsageCount(ports ...int) int {
-	cmd := "sockstat -4c"
-	for _, v := range ports {
-		cmd += " -p " + strconv.Itoa(v)
-	}
-	out, err := exec.Command(cmd).Output()
-	if err != nil {
-		log.Println("Port Usage Check: Could not run ", cmd)
-	}
-	return bytes.Count(out, []byte{'\n'}) - 1
-}
diff --git a/servermanager.go b/servermanager.go
index d91df95..4354376 100644
--- a/servermanager.go
+++ b/servermanager.go
@@ -19,6 +19,11 @@ const (
 	SERVERMANAGER_STOP
 )
 
+type serverManagerEvent struct {
+	eventType int
+	tcpConn   *net.TCPConn
+}
+
 type ServerHandler interface {
 	// Takes snapshot name, and returns the IP to connect to.
 	Spinup(name string) (string, error)
@@ -28,7 +33,8 @@ type ServerHandler interface {
 type ServerManager struct {
 	name       string
 	ports      []int
-	in         chan int
+	gatewayInt string
+	in         chan serverManagerEvent
 	done       chan interface{}
 	usageScore int // spin down server when this reaches 0
 	server     ServerHandler
@@ -39,7 +45,8 @@ func NewServerManager(name string, server ServerHandler, settings Settings) *Ser
 
 	sm.name = name
 	sm.ports = settings.Servers[name].Ports
-	sm.in = make(chan int)
+	sm.gatewayInt = settings.GatewayInterface
+	sm.in = make(chan serverManagerEvent)
 	sm.done = make(chan interface{})
 	sm.usageScore = 5
 	sm.server = server
@@ -68,32 +75,38 @@ func (me *ServerManager) Serve() {
 /*
  Sends the serve loop a spinup message.
 */
-func (me *ServerManager) Spinup() {
-	me.in <- SERVERMANAGER_SPINUP
+func (me *ServerManager) Spinup(c *net.TCPConn) {
+	me.in <- serverManagerEvent{eventType: SERVERMANAGER_SPINUP, tcpConn: c}
 }
 
 /*
  Sends the serve loop a spindown message.
 */
-func (me *ServerManager) Spindown() {
-	me.in <- SERVERMANAGER_SPINDOWN
+func (me *ServerManager) Spindown(c *net.TCPConn) {
+	me.in <- serverManagerEvent{eventType: SERVERMANAGER_SPINDOWN}
 }
 
 /*
  Sends the serve loop a quit message.
 */
 func (me *ServerManager) Stop() {
-	me.in <- SERVERMANAGER_STOP
+	me.in <- serverManagerEvent{eventType: SERVERMANAGER_STOP}
 }
 
 func (me *ServerManager) Done() {
 	<-me.done
 }
 
-func (me *ServerManager) addPortForwards(ip string) {
+func (me *ServerManager) addPortForwards(localIp, remoteIp string) {
+	log.Println("Ports:", me.ports)
+	for _, p := range me.ports {
+		port := strconv.Itoa(p)
+		addPortForward(me.name, me.gatewayInt, localIp, remoteIp, port)
+	}
 }
 
 func (me *ServerManager) rmPortForwards() {
+	rmPortForward(me.name)
 }
 
 func (me *ServerManager) setupListener(port int) {
@@ -118,7 +131,7 @@ func (me *ServerManager) setupListener(port int) {
 					// connection accepted
 
 					// spinup machine
-					me.Spinup()
+					me.Spinup(conn)
 
 					// close existing connection, not doing anything with it
 					conn.Close()
@@ -128,14 +141,15 @@ func (me *ServerManager) setupListener(port int) {
 	}()
 }
 
-func (me *ServerManager) serveAction(action int) bool {
+func (me *ServerManager) serveAction(event serverManagerEvent) bool {
 	running := true
-	switch action {
+	switch event.eventType {
 	case SERVERMANAGER_SPINUP:
-		ip, err := me.server.Spinup(me.name)
+		targetIp, err := me.server.Spinup(me.name)
 		if err == nil {
-			log.Println("ServerManager: Got IP for", me.name+":", ip)
-			me.addPortForwards(ip)
+			log.Println("ServerManager: Got IP for", me.name+":", targetIp)
+			localIp := event.tcpConn.LocalAddr().String()
+			me.addPortForwards(localIp, targetIp)
 		} else {
 			log.Println("ServerManager: Could not spin up "+me.name+":", err)
 		}
diff --git a/settings.go b/settings.go
index 7a63a1b..4620f0c 100644
--- a/settings.go
+++ b/settings.go
@@ -13,8 +13,9 @@ import (
 )
 
 type Settings struct {
-	ApiToken string
-	Servers  map[string]Server
+	ApiToken         string
+	GatewayInterface string
+	Servers          map[string]Server
 }
 
 type Server struct {
@@ -23,6 +24,10 @@ type Server struct {
 	InitialSize string
 	Size        string
 	Region      string
+	ImageSlug   string
+	UserData    string
+	SshKeys     []int
+	Volumes     []string
 }
 
 func loadSettings(path string) (Settings, error) {
diff --git a/varstate.go b/varstate.go
new file mode 100644
index 0000000..d6a86ee
--- /dev/null
+++ b/varstate.go
@@ -0,0 +1,19 @@
+/*
+   Copyright 2016 gtalent2@gmail.com
+
+   This Source Code Form is subject to the terms of the Mozilla Public
+   License, v. 2.0. If a copy of the MPL was not distributed with this
+   file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+package main
+
+// Firewall forwarding rule managed by dospin.
+type ForwardingRule struct {
+	destHost string // Destination IP
+	destPort int
+	srcPort  int
+}
+
+func LoadForwardingRules() []ForwardingRule {
+	return nil
+}