unifi-docker/examples/fixing_backup_errors
Göran Sander d3da6bf047 docs: Add tutorial on how to fix permissions issues that cause backup downloads to fail. 2022-01-21 00:58:10 +01:00
..
README.md docs: Add tutorial on how to fix permissions issues that cause backup downloads to fail. 2022-01-21 00:58:10 +01:00
unifi-backup-1.png docs: Add tutorial on how to fix permissions issues that cause backup downloads to fail. 2022-01-21 00:58:10 +01:00

README.md

Failing to create backups from web UI

This text is about problems relating to backups and downloading of backup files.

Background

Doing backups of your Unifi configuration is important.
Without backups you run the risk of having to re-configure everything if your controller for some reason dies. This can be a massive task for large networks.

This text is an attempt at providing some more context and concrete commands to solve the problems discussed in this issue.

Problem

After doing a fresh install of the Unifi controller using this Docker image you may run into errors when trying to do backups.
Depending on your local setup things may work fine out of the box, but in other cases you can run into this backup problem.

Clicking the "Download Backup" link simply does... nothing.

Backup options in Unifi controller

A closer look into the logs is easy though (adapt container name as needed):

docker logs unifi_logs -f

If you see something like the below you probably suffer from the download-backups-problem:

[2022-01-20T14:00:01,290] <schedule-backup> ERROR system - zipDir error
java.io.FileNotFoundException: /usr/lib/unifi/data/backup/autobackup/autobackup_6.5.55_20220120_1300_1642683600011.unf (No such file or directory)

Configuration

The Docker container was started with this docker-compose.yaml file, which is pretty much standard except for the hard-coded container names:

version: '2.3'
services:
  mongo:
    image: mongo:3.6
    # container_name: ${COMPOSE_PROJECT_NAME}_mongo
    container_name: unifi_mongo
    networks:
      - unifi
    restart: always
    volumes:
      - db:/data/db
      - dbcfg:/data/configdb
  controller:
    # image: "jacobalberty/unifi:${TAG:-latest}"
    image: jacobalberty/unifi:latest
    # container_name: ${COMPOSE_PROJECT_NAME}_controller
    container_name: unifi_controller
    depends_on:
      - mongo
    init: true
    networks:
      - unifi
    restart: always
    volumes:
      - dir:/unifi
      - data:/unifi/data
      - log:/unifi/log
      - cert:/unifi/cert
      - init:/unifi/init.d
      - run:/var/run/unifi
      # Mount local folder for backups and autobackups
      - ./backup:/unifi/data/backup
    user: unifi
    sysctls:
      net.ipv4.ip_unprivileged_port_start: 0
    environment:
      DB_URI: mongodb://mongo/unifi
      STATDB_URI: mongodb://mongo/unifi_stat
      DB_NAME: unifi
      TZ: Europe/Stockholm
    ports:
      - "3478:3478/udp" # STUN
      - "6789:6789/tcp" # Speed test
      - "8080:8080/tcp" # Device/ controller comm.
      - "8443:8443/tcp" # Controller GUI/API as seen in a web browser
      - "8880:8880/tcp" # HTTP portal redirection
      - "8843:8843/tcp" # HTTPS portal redirection
      - "10001:10001/udp" # AP discovery

  logs:
    image: bash
    # container_name: ${COMPOSE_PROJECT_NAME}_logs
    container_name: unifi_logs
    depends_on:
      - controller
    command: bash -c 'tail -F /unifi/log/*.log'
    restart: always
    volumes:
      - log:/unifi/log

volumes:
  db:
  dbcfg:
  data:
  log:
  cert:
  init:
  dir:
  run:

networks:
  unifi:

Investigation

The Docker container will use user unifi to run the controller.
Looking at the /etc/passwed file in the running container we get:

root@abc1:/opt/docker/container# docker exec -it unifi_controller bash
unifi@2ddc781f5259:/unifi$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
unifi:x:999:999::/home/unifi:/bin/sh
mongodb:x:101:102::/var/lib/mongodb:/usr/sbin/nologin
unifi@2ddc781f5259:/unifi$

Looks like the unifi user in the running container has userid=999 and groupid=999.

Now, let's take a look at that path we get an error for in the logs:

unifi@64602e3bbf81:/unifi$ ls -la /usr/lib/unifi/data/backup/
total 8
drwxr-xr-x 2 root  root  4096 Jan 20 13:14 .
drwxr-xr-x 4 unifi unifi 4096 Jan 20 13:42 ..
unifi@64602e3bbf81:/unifi$

See how the current directory (.) has an owner of root:root, whereas the directory above it has unifi:unfi as owner?
That's a clue that there is a permissions problem.

Going back to the docker-compose file, we can see that /unifi/data/backup in the container is mapped to ./backup on the host computer.

Next step is to check the permissions on the host computer for that ./backup directory. It must be readable/writable by the container's unifi:unifi acount - otherwise the code running in the container won't be able to access anything in /unifi/data/backup and below.

root@abc1:/opt/docker/container/unifi-controller# ls -la
total 16
drwxr-xr-x  3 root root 4096 Jan 20 13:14 .
drwxr-xr-x 47 root root 4096 Nov 21 20:04 ..
drwxr-xr-x  2 root root 4096 Jan 20 13:14 backup
-rw-r--r--  1 root root 2590 Jan 20 13:10 docker-compose.yaml
root@abc1:/opt/docker/container/unifi-controller#

Ah! The second last line (for the backup subdirectory) has an owner of root:root (that's the host's root user, btw).
No good. It must be a user on the host with userid:groupid of 999:999.

The solution would then be to create a suitable user on the host computer and then change the owner of the ./backup directory.

Solution

It should be noted that the problem described in this text can be solved in several ways. The container could be made to use another user (that already exists on the host computer), the container could be forced to run as root etc.
What's described below worked in one case, but your milage may vary.

Create unifi user on host computer

The command below is executed as root. Doing a sudo should achieve the same result.

root@abc1: adduser --uid 999 --gid 999 unifi

Changing owner of backup directory on host

root@abc1:/opt/docker/container/unifi-controller# chown -R 999:999 backup
root@abc1:/opt/docker/container/unifi-controller# ls -la
total 16
drwxr-xr-x  3 root  root   4096 Jan 20 13:14 .
drwxr-xr-x 47 root  root   4096 Nov 21 20:04 ..
drwxr-xr-x  3 unifi unifi  4096 Jan 20 18:31 backup
-rw-r--r--  1 root  root   2590 Jan 20 13:10 docker-compose.yaml
root@abc1:/opt/docker/container/unifi-controller#

Great, the ./backup directory now has the permissions needed in order for the container's unifi user to access it.

Testing the solution

Restart the container, then try downloading the backup file again.
Should work - at least if your original issues was the same that lead up to this walk-through...

Automatic backups

Good news: The work above also fixes automatic backups.

The automatic backups rely on files in the same backup directory structure within the container as the manual backups.

Give it a try: Set a time for automatic backups and check back once that time has passed. There should now be a downloadable backup file in the web UI.
Examples of such files are shown in the screen shot above.