             tables = (t for t,s of SCHEMA when not s.virtual)
         dbg = @_dbg("backup_tables()")
         dbg("backing up tables: #{misc.to_json(tables)}")
-        backup = (table, cb) =>
-            dbg("backup '#{table}'")
-            @_backup_table
-                table : table
-                path  : opts.path
-                cb    : cb
-        async.mapLimit(tables, opts.limit, backup, (err)=>opts.cb(err))
+        async.series([
+            (cb) =>
+                backup = (table, cb) =>
+                    dbg("backup '#{table}'")
+                    @_backup_table
+                        table : table
+                        path  : opts.path
+                        cb    : cb
+                async.mapLimit(tables, opts.limit, backup, cb)
+            (cb) =>
+                @_backup_bup
+                    path : opts.path
+                    cb   : cb
+        ], (err) => opts.cb(err))
 
     _backup_table: (opts) =>
         opts = defaults opts,

                PGPASSWORD : @_password
                PGUSER     : 'smc'
                PGHOST     : @_host
            err_on_exit : true
            cb      : opts.cb


            tables : undefined  # if not given, backs up all tables; can also be a list of tables
            path   : 'backup'
            limit  : 3          # number of tables to backup in parallel
            bup    : true       # creates/updates a bup archive in backup/.bup,
                                # so we have snapshots of all past backups!
            cb     : required
        if opts.tables?
            tables = opts.tables
