diff --git a/etcdmain/config.go b/etcdmain/config.go
index a138071a9..c9d65e0f6 100644
--- a/etcdmain/config.go
+++ b/etcdmain/config.go
@@ -17,6 +17,7 @@
 package etcdmain
 
 import (
+	"errors"
 	"flag"
 	"fmt"
 	"log"
@@ -27,6 +28,7 @@ import (
 	"github.com/coreos/etcd/etcdserver"
 	"github.com/coreos/etcd/pkg/cors"
 	"github.com/coreos/etcd/pkg/flags"
+	"github.com/coreos/etcd/pkg/netutil"
 	"github.com/coreos/etcd/pkg/transport"
 	"github.com/coreos/etcd/version"
 )
@@ -242,9 +244,17 @@ func (cfg *config) Parse(arguments []string) error {
 		return err
 	}
 
+	if err := cfg.resolveUrls(); err != nil {
+		return errors.New("cannot resolve DNS hostnames.")
+	}
+
 	return nil
 }
 
+func (cfg *config) resolveUrls() error {
+	return netutil.ResolveTCPAddrs(cfg.lpurls, cfg.apurls, cfg.lcurls, cfg.acurls)
+}
+
 func (cfg config) isNewCluster() bool          { return cfg.clusterState.String() == clusterStateFlagNew }
 func (cfg config) isProxy() bool               { return cfg.proxy.String() != proxyFlagOff }
 func (cfg config) isReadonlyProxy() bool       { return cfg.proxy.String() == proxyFlagReadonly }
diff --git a/pkg/netutil/netutil.go b/pkg/netutil/netutil.go
new file mode 100644
index 000000000..655bf1902
--- /dev/null
+++ b/pkg/netutil/netutil.go
@@ -0,0 +1,57 @@
+/*
+   Copyright 2014 CoreOS, Inc.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package netutil
+
+import (
+	"log"
+	"net"
+	"net/url"
+)
+
+var (
+	// indirection for testing
+	resolveTCPAddr = net.ResolveTCPAddr
+)
+
+// ResolveTCPAddrs is a convenience wrapper for net.ResolveTCPAddr.
+// ResolveTCPAddrs resolves all DNS hostnames in-place for the given set of
+// url.URLs.
+func ResolveTCPAddrs(urls ...[]url.URL) error {
+	for _, us := range urls {
+		for i, u := range us {
+			host, _, err := net.SplitHostPort(u.Host)
+			if err != nil {
+				log.Printf("netutil: Could not parse url %s during tcp resolving.", u.Host)
+				return err
+			}
+			if host == "localhost" {
+				continue
+			}
+			if net.ParseIP(host) != nil {
+				continue
+			}
+			tcpAddr, err := resolveTCPAddr("tcp", u.Host)
+			if err != nil {
+				log.Printf("netutil: Could not resolve host: %s", u.Host)
+				return err
+			}
+			log.Printf("netutil: Resolving %s to %s", u.Host, tcpAddr.String())
+			us[i].Host = tcpAddr.String()
+		}
+	}
+	return nil
+}
diff --git a/pkg/netutil/netutil_test.go b/pkg/netutil/netutil_test.go
new file mode 100644
index 000000000..e2a50d8cc
--- /dev/null
+++ b/pkg/netutil/netutil_test.go
@@ -0,0 +1,140 @@
+/*
+   Copyright 2014 CoreOS, Inc.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package netutil
+
+import (
+	"errors"
+	"net"
+	"net/url"
+	"reflect"
+	"strconv"
+	"testing"
+)
+
+func TestResolveTCPAddrs(t *testing.T) {
+	defer func() { resolveTCPAddr = net.ResolveTCPAddr }()
+	tests := []struct {
+		urls     [][]url.URL
+		expected [][]url.URL
+		hostMap  map[string]string
+		hasError bool
+	}{
+		{
+			urls: [][]url.URL{
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "127.0.0.1:4001"},
+					url.URL{Scheme: "http", Host: "127.0.0.1:2379"},
+				},
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "127.0.0.1:7001"},
+					url.URL{Scheme: "http", Host: "127.0.0.1:2380"},
+				},
+			},
+			expected: [][]url.URL{
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "127.0.0.1:4001"},
+					url.URL{Scheme: "http", Host: "127.0.0.1:2379"},
+				},
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "127.0.0.1:7001"},
+					url.URL{Scheme: "http", Host: "127.0.0.1:2380"},
+				},
+			},
+		},
+		{
+			urls: [][]url.URL{
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "infra0.example.com:4001"},
+					url.URL{Scheme: "http", Host: "infra0.example.com:2379"},
+				},
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "infra0.example.com:7001"},
+					url.URL{Scheme: "http", Host: "infra0.example.com:2380"},
+				},
+			},
+			expected: [][]url.URL{
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "10.0.1.10:4001"},
+					url.URL{Scheme: "http", Host: "10.0.1.10:2379"},
+				},
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "10.0.1.10:7001"},
+					url.URL{Scheme: "http", Host: "10.0.1.10:2380"},
+				},
+			},
+			hostMap: map[string]string{
+				"infra0.example.com": "10.0.1.10",
+			},
+			hasError: false,
+		},
+		{
+			urls: [][]url.URL{
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "infra0.example.com:4001"},
+					url.URL{Scheme: "http", Host: "infra0.example.com:2379"},
+				},
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "infra0.example.com:7001"},
+					url.URL{Scheme: "http", Host: "infra0.example.com:2380"},
+				},
+			},
+			hostMap: map[string]string{
+				"infra0.example.com": "",
+			},
+			hasError: true,
+		},
+		{
+			urls: [][]url.URL{
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "ssh://infra0.example.com:4001"},
+					url.URL{Scheme: "http", Host: "ssh://infra0.example.com:2379"},
+				},
+				[]url.URL{
+					url.URL{Scheme: "http", Host: "ssh://infra0.example.com:7001"},
+					url.URL{Scheme: "http", Host: "ssh://infra0.example.com:2380"},
+				},
+			},
+			hasError: true,
+		},
+	}
+	for _, tt := range tests {
+		resolveTCPAddr = func(network, addr string) (*net.TCPAddr, error) {
+			host, port, err := net.SplitHostPort(addr)
+			if err != nil {
+				return nil, err
+			}
+			if tt.hostMap[host] == "" {
+				return nil, errors.New("cannot resolve host.")
+			}
+			i, err := strconv.Atoi(port)
+			if err != nil {
+				return nil, err
+			}
+			return &net.TCPAddr{IP: net.ParseIP(tt.hostMap[host]), Port: i, Zone: ""}, nil
+		}
+		err := ResolveTCPAddrs(tt.urls...)
+		if tt.hasError {
+			if err == nil {
+				t.Errorf("expected error")
+			}
+			continue
+		}
+		if !reflect.DeepEqual(tt.urls, tt.expected) {
+			t.Errorf("expected: %v, got %v", tt.expected, tt.urls)
+		}
+	}
+}
diff --git a/test b/test
index 1a87669e5..c2ad2c009 100755
--- a/test
+++ b/test
@@ -15,7 +15,7 @@ COVER=${COVER:-"-cover"}
 source ./build
 
 # Hack: gofmt ./ will recursively check the .git directory. So use *.go for gofmt.
-TESTABLE_AND_FORMATTABLE="client discovery error etcdctl/command etcdmain etcdserver etcdserver/etcdhttp etcdserver/etcdhttp/httptypes etcdserver/etcdserverpb integration migrate pkg/fileutil pkg/flags pkg/ioutils pkg/types pkg/transport pkg/wait proxy raft rafthttp snap store wal"
+TESTABLE_AND_FORMATTABLE="client discovery error etcdctl/command etcdmain etcdserver etcdserver/etcdhttp etcdserver/etcdhttp/httptypes etcdserver/etcdserverpb integration migrate pkg/fileutil pkg/flags pkg/ioutils pkg/netutil pkg/types pkg/transport pkg/wait proxy raft rafthttp snap store wal"
 FORMATTABLE="$TESTABLE_AND_FORMATTABLE *.go etcdctl/"
 
 # user has not provided PKG override