Sometimes you just need that one file, and it'd be really nice if you just had a simple application
that ran in a web browser that gave you access to your files.
Well, today we're going to be taking a look at CopyParty.
This turns almost any device into a file server.
It's a really simple Python application that did the rounds a few months ago.
It's just taken me a while to get around to covering it.
And this application we're going to run in a container.
We're going to put on our tail nets, of course, because that's what we do here on the TailScale channel.
I'm also going to show you some new Docker Compose secrets, tricks of how you can configure
not only the CopyParty application itself directly in your Compose YAML files,
but also the TailScaleServe sidecar piece directly in your Docker Compose YAMLs as well.
Now, I'll put a link in the description to all of the materials about CopyParty,
such as this fantastic YouTube video, 16 minutes long,
it goes through all of the different features
that CopyParty has.
It's brilliant.
It does a better job than I could possibly manage.
We're going to focus, like I say,
on the Docker Compose aspects today
and deploying this thing onto your tail net.
Now, you've seen on the channel many times before
that I deploy applications with the main application
and then a tailscale sidecar container.
And the downside of that, in fact,
if you look in this long list of apps
we've covered on this channel over the years. The downside of that is that you need this kind of
sidecar volume mounted serve.json file. It's a very simple file. All it essentially does is it tells
the tailscale serve to listen on port 443 to proxy this port from inside the container onto your
tail net using tailscale serve. I've always wished that it was a simple way to do that without bind
mounting a separate file well i found one i found one it's been right under my nose this whole time
docker compose has this configs top level element built into the yaml configuration schema of docker
compose so what we can do is we can define okay this is just the json blob we looked at earlier
kind of minified ish so that it fits more comfortably inside our compose file and doesn't
like WordWrap or anything.
What we can do is we can define these elements
inside our config files,
and then we can reference them later on
inside our configurations.
That's it, that's the cool trick.
So what this does is it ingests this content block up here,
lines four through eight,
and then spits it out into a file
inside the remote container.
So let's take a little look
at the copy party application itself.
It is super basic.
It's just a file browser that runs inside a web browser.
Effectively, you know, I can select an image,
I can view an image, I can go through here
and look at my obsession with the Volkswagen Golf.
This was Wookiees in the Woods, by the way, a few years ago.
Fantastic event if you're a Volkswagen enthusiast like me.
And you know, I can see all of my images
that I've taken over the years.
In fact, these are the same images
that were powering the image video that came out last week.
So I'm gonna log in now using the username and password
configured in my compose file.
It literally right now is change me.
And now I just want to create a new directory.
I'm just going to call this test
because I want to show you how fast this thing is.
I'm going to bring up a directory full of a bunch of test photos.
And then if I just drop this into the browser like so, ready?
Here we go.
We're uploading, I don't know, 25 files, 22 files.
And that's it, it's done.
And we can now bring these up full screen and have a look at them.
We can download them.
We can fetal them.
We can rotate them.
It also supports rich media.
So if you've got an audio file, for example, let me look on my desktop,
where I've almost certainly got a recording that I'm using for this video.
Let me just upload that here.
And we can play those audio files in the browser.
There we go.
So now I could use this as a low-rent podcast player if I wanted to.
The other option it does as well, can I see?
Yeah, it renders the spectral waveform of the file.
I just think that's a really cool,
nice little touch from CopyParty.
There are tons and tons and tons
of other features to this thing.
For example, if I wanted to download
the entire directory as a zip file,
which can be really handy sometimes,
I just click this little button down here.
No right-click menus, no doing silliness,
and there you go.
There's my test directory now as a zip file
on my local system.
There is a way you can mount CopyParty as a webdav,
So if you go up to this option up here and say connect,
you can basically turn your copy party instance into like a remote file server
that appears as a mounted network share inside your computer.
And you can, of course, do this all over TailScale when it's on your TailNet.
And he supports multiple different ways.
You've got WebDAV, you've got PartyFuse, iShare,
as well as all the different operating systems using Rclone
and all the other stuff that exists in the world.
I can't say enough good things about this app.
Honestly, it's really simple.
It's really simple considering it's just a bunch of Python.
And it just does what it needs to do really well.
So let's take a little look at some of the lifecycle stuff
around the config stanzas inside Docker Compose.
You can see that, as I showed you earlier,
we have the tailscale serve configuration,
but we also have the copy party configuration as well.
It's a very simple application to configure.
And so I thought it was a perfect example for today's video.
You can see how the config for TS serve gets reused later on in the file just down here.
And then the same thing applies also to the copy party configuration as well.
So how does this get passed through on the remote side?
What does it look like inside the container?
Well, let's take a look at the copy party TS serve container.
We're going to look inside copy party TS.
Now I've SSH'd into fake NAS, which is where this application is actually running.
And I'm going to do this command, docker exec copy party TS.
and I want to see what the config looks like,
the serve config looks like inside that remote container.
Because if we look at the YAML file look,
this gets passed through and mapped almost as a target
in /config/serve.json, which is the file path here
that I'm going to print out to the terminal
and we're going to have a look at.
And you can see this block here matches perfectly
this block here inside my compose file.
But what if I want to make a change?
This is where it can get a little bit, I don't know,
like I just expected it to work a different way than what it actually does.
But we'll get there.
Let's make the change to the compose.yaml file.
And I want to change tailscalefunnel to be true for this node, for example.
Now, if I do that and modify the compose file and then do a Docker compose restart,
I've restarted the copyparty and the copyparty tailscale containers, the sidecast.
But if I do the same exec command look, it still says false.
even though our compose file says, our survey says,
even though our compose file says true right here.
It does seem logical that that would work, doesn't it?
But the problem here is that the restart process
only stops the process inside the container
and then starts the container again.
It doesn't run any of the container creation
or instantiation routines that look at the config files
in your YAML file.
It doesn't recompute the hash and say,
hey, something's changed.
We should probably update this.
The container itself will still exist, the same container, the same file system, the same mounted configuration object from when it was first created.
Compose won't go back and read that config file until you recreate the container.
So that leaves us with really only a couple of options.
And the one that I've sort of landed on is to use Docker Compose up force recreate.
So this is a lifecycle issue.
So if we can do a force recreate, then we're all good to go.
So I'm going to do this.
I'm going to do docker compose up minus D dash dash force recreate.
And look what happens to our YAML file.
Our funnel block now reads true.
So if you're using these config blocks to define configs for applications,
you need to understand that they work differently to bind mounts.
They don't update in real time inside the container.
Docker Compose only reads these config blocks at container creation time, at container instantiation time.
So to make a change to those underlying configs and for them to be passed through to the containers,
you just have to recreate the container every time.
It's a small workflow tweak, but a really, really important one.
So that's kind of the gotcha with Docker Compose configs.
They're read once when the container is created,
and then they're locked in until you recreate that container completely.
It's not obvious, and the docs don't exactly make this crystal clear, that whole life cycle of the container piece.
So hopefully this saves some of you from the debugging headache that I had today.
So let me know in the comments what other stuff is hiding in the Docker Compose documentation that I've probably completely missed.
This has been one of my favorite little discoveries of the last few weeks.
Now, I have to say a great big thank you to all of you for watching throughout 2025.
And here is to a prosperous and successful 2026 to all of you.
Thank you so much for watching.
I've been Alex from Tailscale.