diff --git a/pseudo.1 b/pseudo.1
index 2475631..be0827f 100644
--- a/pseudo.1
+++ b/pseudo.1
@@ -703,6 +703,9 @@ database files and log files; by default, it is the
 .I var/pseudo
 directory under
 .BR PSEUDO_PREFIX .
+It must be writable; if it is not,
+.I pseudo
+fails at startup rather than trying to start the server.
 .TP 8
 .B PSEUDO_NOSYMLINKEXP
 By default, when chrooted,
diff --git a/pseudo_client.c b/pseudo_client.c
index efb5662..44a6b88 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -608,6 +608,16 @@ pseudo_init_client(void) {
 					strerror(errno));
 				exit(1);
 			}
+			/* the server can't run without creating its lock,
+			 * socket, pid and database files here, so fail now
+			 * instead of repeatedly trying to start it
+			 */
+			if (faccessat(pseudo_localstate_dir_fd, ".", W_OK | X_OK, 0) == -1) {
+				pseudo_error("Local state path '%s' is not writable: %s\n",
+					pseudo_path,
+					strerror(errno));
+				exit(1);
+			}
 		}
 		free(pseudo_path);
 
@@ -1028,6 +1038,22 @@ client_spawn_server(void) {
 		} else {
 			pseudo_evlog(PDBGF_CLIENT, "server startup apparently unsuccessful.  setting server pid to -1.\n");
 			server_pid = -1;
+			/* Retrying can't fix these, so stop instead of
+			 * respawning the server PSEUDO_RETRIES times.
+			 */
+			if (WIFEXITED(status)) {
+				switch (WEXITSTATUS(status)) {
+				case PSEUDO_EXIT_LOCK_PATH:
+				case PSEUDO_EXIT_PSEUDO_LOADED:
+				case PSEUDO_EXIT_PSEUDO_PREFIX:
+				case PSEUDO_EXIT_PSEUDO_INVOCATION:
+					pseudo_critical("server failed to start: %s\n",
+						pseudo_exit_status_name(WEXITSTATUS(status)));
+					break;
+				default:
+					break;
+				}
+			}
 			return 1;
 		}
 	} else {
diff --git a/pseudo_server.c b/pseudo_server.c
index 7016adc..79365e6 100644
--- a/pseudo_server.c
+++ b/pseudo_server.c
@@ -266,6 +266,11 @@ pseudo_server_start(int daemonize) {
 		exit(PSEUDO_EXIT_LOCK_PATH);
 	}
 	mkdir_p(lockpath);
+	if (access(lockpath, W_OK | X_OK) == -1) {
+		pseudo_error("Local state directory '%s' is not writable, unable to create lockfile: %s\n",
+			lockpath, strerror(errno));
+		exit(PSEUDO_EXIT_LOCK_FAILED);
+	}
 	lockname = pseudo_localstatedir_path(PSEUDO_LOCKFILE);
 	if (!lockname) {
 		pseudo_error("Couldn't allocate a file path.\n");
