Merge branch 'NextcloudS3Migration' into 'main'
Document Nextcloud S3 Storage See merge request d_mcknight/blog-content!6
This commit is contained in:
		
						commit
						0859da10d7
					
				
					 2 changed files with 192 additions and 0 deletions
				
			
		
							
								
								
									
										192
									
								
								2024-03-28_Nextcloud-S3-Migration.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								2024-03-28_Nextcloud-S3-Migration.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,192 @@ | ||||||
|  | --- | ||||||
|  | date: 2024-03-28 | ||||||
|  | title: Migrating Nextcloud from Local Storage to S3 | ||||||
|  | tags:  | ||||||
|  |   - homelab | ||||||
|  |   - s3 | ||||||
|  |   - Nextcloud | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | As I continue to investigate [migration from Unraid to XCP-ng](https://blog.mcknight.tech/2024/01/30/Compute_Setup/) | ||||||
|  | (or maybe Proxmox), I am taking a closer look at what storage resources my containers are still using on my Unraid | ||||||
|  | system. Unsurprisingly, Nextcloud is using a significant amount of persistent storage and I found that moving that | ||||||
|  | data to a remote share is difficult due to Nextcloud's file permission requirements. Looking for a solution, I found | ||||||
|  | that Nextcloud supports S3 storage and, while not officially supported, there are ways to move from local storage to | ||||||
|  | S3 without having to start over from scratch. I will admit that much of this was heacily inspired by other guides  | ||||||
|  | which I've link below, but there were a few gotchas related to doing this with containers in Unraid that I'll highlight. | ||||||
|  | 
 | ||||||
|  | ## Why to migrate storage | ||||||
|  | As I mentioned, my primary reason for migrating from local to S3 storage is to simplify running Nextcloud in a  | ||||||
|  | container with primary storage residing on a different system. This is also a fun project for me to learn a  | ||||||
|  | little more about how S3 works (this is my first application using S3 storage 🎉). I'm using MinIO on TrueNAS | ||||||
|  | which was practically a one-click deployment; I followed  | ||||||
|  | [this video from Lawrence Systems](https://youtu.be/uIm41PhGEgQ?si=0X1FZgsNfZisnaKw). | ||||||
|  | 
 | ||||||
|  | ## Preparing for migration | ||||||
|  | Fist things first, configure a bucket for Nextcloud (and only Nextcloud) to use; I've named mine "nextcloud". | ||||||
|  | I only have a few users on my Nextcloud instance and I'm the only one who uses it regularly, so I was able to | ||||||
|  | prevent any changes during the migration by closing any desktop applications that sync and not taking any | ||||||
|  | photos on my phone. If you have more users or activity, I would recommend putting Nextcloud into maintenance | ||||||
|  | mode before migrating things (`occ maintenance:mode --on`). | ||||||
|  | 
 | ||||||
|  | ## Migrating from local to S3 | ||||||
|  | Other guides I found made some assumptions about Nextcloud and SQL running in the same environment, which | ||||||
|  | isn't true in my case where they're in containers. They also assumed the `aws` cli could be used where | ||||||
|  | Nextcloud is running which is also not true in my case since Unraid doesn't have that package available.  | ||||||
|  | The code snippets below have been modified from other examples to fit my use case. | ||||||
|  | 
 | ||||||
|  | ### Get files from SQL | ||||||
|  | My Nextcloud setup uses MariaDb, so the first step is to export the list of files, along with some identifiers | ||||||
|  | Nextcloud uses, to be uploaded. My database is called `nextcloud` and the `/config` directory is mounted to the | ||||||
|  | host. `/mnt/user/Nextcloud` on the host system is mounted to `/data` in the Nextcloud container. | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | mysql -p -B --disable-column-names -D nextcloud << EOF > /config/meta_file_list    | ||||||
|  |       select concat('urn:oid:', fileid, ' ', substring(id from 8), path) | ||||||
|  |       from oc_filecache | ||||||
|  |       join oc_storages | ||||||
|  |      on storage = numeric_id | ||||||
|  |      where id like 'local::%' | ||||||
|  |      order by id; | ||||||
|  | EOF | ||||||
|  | 
 | ||||||
|  | mysql -p -B --disable-column-names -D nextcloud << EOF > /config/user_file_list    | ||||||
|  |       select concat('urn:oid:', fileid, ' ', '/mnt/user/Nextcloud/', | ||||||
|  |                  substring(id from 7), '/', path)      | ||||||
|  |       from oc_filecache      | ||||||
|  |       join oc_storages       | ||||||
|  |      on storage = numeric_id    | ||||||
|  |      where id like 'home::%'    | ||||||
|  |      order by id; | ||||||
|  | EOF | ||||||
|  | 
 | ||||||
|  | sed -i -e "s| /data/| /mnt/user/Nextcloud/|g" meta_file_list | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Note that `user_file_list` paths are updated in the `mysql` command and the `meta_file_list` is updated | ||||||
|  | by the `sed` command to be accessible on the host. | ||||||
|  | 
 | ||||||
|  | ### Move files to upload directory | ||||||
|  | Since I will be mounting the upload directory to a virtual machine, I opted to copy files rather than  | ||||||
|  | create symlinks like the example I used as reference. It doesn't particularly matter where on the host | ||||||
|  | system the files to upload are copied to, but I used `/mnt/user/Nextcloud` which was my existing share | ||||||
|  | for Nextcloud data. I copied `user_file_list` and `meta_file_list` from the previous step to that path. | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | cd /mnt/user/Nextcloud | ||||||
|  | mkdir s3_files | ||||||
|  | cd s3_files | ||||||
|  | 
 | ||||||
|  | while read target source ; do | ||||||
|  |     if [ -f "$source" ] ;  | ||||||
|  | then | ||||||
|  |         cp "$source" "$target" | ||||||
|  |     fi | ||||||
|  | done < ../user_file_list | ||||||
|  | 
 | ||||||
|  |  while read target source ; do | ||||||
|  |     if [ -f "$source" ] ; | ||||||
|  |  then | ||||||
|  |         cp "$source" "$target" | ||||||
|  |     fi | ||||||
|  | done < ../meta_file_list | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | At this point, all of the files are named and placed in the `s3_files` directory, ready to be uploaded | ||||||
|  | to S3. Note that since the filenames contain `:`, this directory will NOT be accessible via an SMB share. | ||||||
|  | I exported it as an NFS share, but if I could install `awscli` on the host system then it wouldn't have | ||||||
|  | been a concern. | ||||||
|  | 
 | ||||||
|  | On my VM where I actually uploaded everything to a bucket named `nextcloud`: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | sudo mount <SERVER_ADDR>:/mnt/user/Nextcloud /home/$USER/Nextcloud | ||||||
|  | cd ~/Nextcloud | ||||||
|  | aws --endpoint-url <S3_URL> s3 sync s3_files s3://nextcloud | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Nextcloud config updates | ||||||
|  | With all of the files uploaded to S3, Nextcloud needs to be updated to point at the new data source. So, | ||||||
|  | time to put Nextcloud into maintenance mode and start breaking things 🙂. From here on out, we are making  | ||||||
|  | potentially destructive changes, so make backups, have a recovery plan, and don't continue | ||||||
|  | until reading to the end first. | ||||||
|  | 
 | ||||||
|  | In the Docker container: | ||||||
|  | 
 | ||||||
|  | ```shell | ||||||
|  | occ maintenance:mode --on | ||||||
|  | nano /config/www/nextcloud/config/config.php | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Add the following to the Nextcloud config (`/config/www/nextcloud/config/config.php`): | ||||||
|  | 
 | ||||||
|  | ```php | ||||||
|  |   'objectstore' => | ||||||
|  |   array ( | ||||||
|  |     'class' => 'OC\\Files\\ObjectStore\\S3', | ||||||
|  |     'arguments' => | ||||||
|  |     array ( | ||||||
|  |       'bucket' => 'nextcloud', | ||||||
|  |       'autocreate' => true, | ||||||
|  |       'key' => '<MY_AWS_KEY>', | ||||||
|  |       'secret' => '<MY_AWS_SECRET>', | ||||||
|  |       'hostname' => '<S3_ADDRESS>', | ||||||
|  |       'port' => 443, | ||||||
|  |       'use_ssl' => true, | ||||||
|  |       'region' => 'home', | ||||||
|  |       'use_path_style' => true, | ||||||
|  |     ), | ||||||
|  |   ), | ||||||
|  | ``` | ||||||
|  | > Make sure this is before the closing `);` at the end of the file | ||||||
|  | 
 | ||||||
|  | `use_path_style` was required for MinIO, but I read that this may be false for other providers. | ||||||
|  | 
 | ||||||
|  | ### SQL database updates | ||||||
|  | The SQL database at this point still contains local file references, so those will need to be | ||||||
|  | updated to the uploaded resources. In the SQL container: | ||||||
|  | ```shell | ||||||
|  | mariadb -p -D nextcloud -e "update oc_storages set id = concat('object::user:', substring(id from 7)) where id like 'home::%';" | ||||||
|  | mariadb -p -D nextcloud -e "update oc_storages set id = 'object::store:amazon::nextcloud' where id like 'local::%';" | ||||||
|  | mariadb -p -D nextcloud -e "update oc_mounts set mount_provider_class = 'OC\\\Files\\\Mount\\\ObjectHomeMountProvider' where mount_provider_class like '%LocalHomeMountProvider%';" | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Validating Changes | ||||||
|  | With configuration and database changes completed, its time to take Nextcloud out of maintenance mode | ||||||
|  | and validate changes. In the Nextcloud container: | ||||||
|  | ```shell | ||||||
|  | occ maintenance:mode --off | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | > I also restarted the Nextcloud container here for good measure, but it may not be necessary. | ||||||
|  | 
 | ||||||
|  | Load up Nextcloud in a browser and make sure the page loads, user login works, and most importantly | ||||||
|  | user files are accessible. If this all works, then the migration is complete! | ||||||
|  | 
 | ||||||
|  | ## Cleanup local files | ||||||
|  | With migration completed, I chose to remove the `data` container mount since the directory only needs | ||||||
|  | the `.ocdata` file and should only contain logs. On the host system: | ||||||
|  | ```shell | ||||||
|  | cp /mnt/user/Nextcloud/.oc_data /mnt/user/appdata/nextcloud/data/ | ||||||
|  | chmod 770 /mnt/user/appdata/nextcloud/data/ | ||||||
|  | chown nobody:users /mnt/user/appdata/nextcloud/data/ | ||||||
|  | ``` | ||||||
|  | > Note that the ownership is specific to Unraid, but Nextcloud requires permissions `770`. | ||||||
|  | 
 | ||||||
|  | Then, modify Nextcloud's `config.php`: | ||||||
|  | ```php | ||||||
|  | 'datadirectory' => '/config/data', | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Then the `/data` mount can be removed from the Docker container and the container restarted. At this point, | ||||||
|  | the old data share can be removed completely (I'll archive mine as a backup). | ||||||
|  | 
 | ||||||
|  | ## Wrap up | ||||||
|  | I have been using my Nextcloud with S3 storage for a few days now and haven't noticed any difference compared | ||||||
|  | to local storage. I like that the storage is accessible via HTTP so I don't have to worry about  | ||||||
|  | managing permissions and access from the Nextcloud container to my storage server. | ||||||
|  | 
 | ||||||
|  | ## References | ||||||
|  | - [mrAceT migration scripts](https://github.com/mrAceT/nextcloud-S3-local-S3-migration) | ||||||
|  | - [Nextcloud issue with guide](https://github.com/nextcloud/server/issues/25781) | ||||||
|  | - [Nextcloud forum thread](https://help.nextcloud.com/t/migrate-from-local-storage-to-s3/138042) | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue