diff --git a/.dockerignore b/.dockerignore
index 3c3629e647f5ddf82548912e337bea9826b434af..9e680f5ae425d03a63b77ea65c7477df05eddadf 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1 +1,3 @@
 node_modules
+user-files
+server-files
diff --git a/.gitignore b/.gitignore
index 46e1ad47f03cef7d31558285361d5c274f68b412..19faa0c1d8628f71c89c7e1feaa8e42d21c7db04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ log
 supervise
 bin/large-sync-data.txt
 user-files
-server-files
\ No newline at end of file
+server-files
+fly.toml
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 065317a7377fb468bf590c8ce2a2670a87ba1c6f..844f6af91c7865fd0e9d4dc51cdc692ad0fd9bde 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,5 +10,8 @@ ENV NODE_ENV=production
 ADD . .
 
 RUN yarn install --production
+RUN mkdir ./server-files
+RUN mkdir ./user-files
+RUN cp ./sql/default-account.sqlite ./server-files/account.sqlite
 
 CMD ["yarn", "start"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 5981df45a03b0030e88d049fa21d1d98c0ae7cb9..733600c7c27d5e9a2199c7144b53c75fb265f8a5 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ You should deploy your server so it's always running. We recommend [fly.io](http
 
 Next, [install the `flyctl`](https://fly.io/docs/flyctl/installing/) utility. Run `flyctl auth login` to sign into your account.
 
-Open `fly.toml` and customize the app name on the first line of the file.
+Copy `fly.template.toml` to `fly.toml`. Open `fly.toml` and customize the app name on the first line of the file.
 
 Now, run `flyctl launch` from `actual-server`. You should have a running app now!
 
@@ -36,10 +36,32 @@ Whenever you want to update Actual, update the versions of `@actual-app/api` and
 
 **Note:** if you don't want to use fly, we still provide a `Dockerfile` to build the app so it should work anywhere that can compile a docker image.
 
+### Persisting server data
+
+One problem with the above setup is every time you deploy, it will wipe away all the data on the server. You'll need to bootstrap the instance again and upload your files.
+
+Let's move the data somewhere that persists. With [fly.io](https://fly.io) we can create a [volume](https://fly.io/docs/reference/volumes/). Run this command:
+
+```
+flyctl volumes create actual_data
+```
+
+Now we need to tell Actual to use this volume. Add this in `fly.toml`:
+
+```
+[mounts]
+  source="actual_data"
+  destination="/data"
+```
+
+That's it! Actual will automatically check if the `/data` directory exists and use it automatically.
+
+_You can also configure the data dir with the `ACTUAL_USER_FILES` environment variable._
+
 ## Configuring the server URL
 
 The Actual app is totally separate from the server. In this project, they happen to both be served by the same server, but the app doesn't know where the server lives.
 
 The server could live on a completely different domain. You might setup Actual so that the app and server are running in completely separate places.
 
-Since Actual doesn't know what server to use, the first thing it does is asks you for the server URL. If you are running this project, simply click "Use this domain" and it will automatically fill it in with the current domain. This works because we are serving the app and server in the same place.
\ No newline at end of file
+Since Actual doesn't know what server to use, the first thing it does is asks you for the server URL. If you are running this project, simply click "Use this domain" and it will automatically fill it in with the current domain. This works because we are serving the app and server in the same place.
diff --git a/fly.template.toml b/fly.template.toml
new file mode 100644
index 0000000000000000000000000000000000000000..6bd5f2dc9b1c56deec4851826361de954cd7dfc0
--- /dev/null
+++ b/fly.template.toml
@@ -0,0 +1,39 @@
+app = "%NAME%"
+
+kill_signal = "SIGINT"
+kill_timeout = 5
+processes = []
+
+[env]
+  PORT = "8080"
+
+[experimental]
+  allowed_public_ports = []
+  auto_rollback = true
+
+[[services]]
+  http_checks = []
+  internal_port = 5006
+  processes = ["app"]
+  protocol = "tcp"
+  script_checks = []
+
+  [services.concurrency]
+    hard_limit = 25
+    soft_limit = 20
+    type = "connections"
+
+  [[services.ports]]
+    force_https = true
+    handlers = ["http"]
+    port = 80
+
+  [[services.ports]]
+    handlers = ["tls", "http"]
+    port = 443
+
+  [[services.tcp_checks]]
+    grace_period = "1s"
+    interval = "15s"
+    restart_limit = 0
+    timeout = "2s"
diff --git a/fly.toml b/fly.toml
index 6bd5f2dc9b1c56deec4851826361de954cd7dfc0..1656b7f72feab2c03aeeaaf811738b32146745c5 100644
--- a/fly.toml
+++ b/fly.toml
@@ -1,4 +1,4 @@
-app = "%NAME%"
+app = "actual-server"
 
 kill_signal = "SIGINT"
 kill_timeout = 5
@@ -11,6 +11,10 @@ processes = []
   allowed_public_ports = []
   auto_rollback = true
 
+[mounts]
+  source="actual_data"
+  destination="/data"
+
 [[services]]
   http_checks = []
   internal_port = 5006
diff --git a/load-config.js b/load-config.js
index c4258225ad247b651858a56af255a5547d0e354e..4af565dc8749b4e6ae72e37e29df9fe105f75c81 100644
--- a/load-config.js
+++ b/load-config.js
@@ -2,10 +2,12 @@ let config;
 try {
   config = require('./config');
 } catch (e) {
+  let fs = require('fs');
+
   config = {
     mode: 'development',
     port: 5006,
-    files: './user-files'
+    files: fs.existsSync('/data') ? '/data' : './user-files'
   };
 }
 
diff --git a/sql/default-account.sqlite b/sql/default-account.sqlite
new file mode 100644
index 0000000000000000000000000000000000000000..3878e6831c5c80d9af8539f4b3fa7fcc3de6b916
Binary files /dev/null and b/sql/default-account.sqlite differ