Setting up ZSH in Docker
Tags: docker, zsh
Published: 2021-04-17 06:28:38

I love ZSH. I love my colorful terminal prompts, I love my indicators, I love my icons. That's all very well on my computer, but what about inside Docker containers?

Well, turns out it's actually really easy to set up ZSH in Docker, it just requires a bit of doing. This post will explain how to do just that! I'll use a non-root user because that's safer, but if you want to use `root` to do everything, just ignore the user switching and permissions changing, it should all work fine.

I've set this up as a multi-stage build layer, but you can get rid of the `AS setup` in the first line if you want to do something else.

**TL;DR:** Here's the Gist! (If you're using NodeJS, there's a special one for you [here](https://gist.github.com/arctic-hen7/2281c59d369df0a472cb0d457f6c47c8)!)
{% gist https://gist.github.com/arctic-hen7/10987790b86360820e2790650e289f0b %}

Let's break that down. We start off by using the Alpine Linux image, but this should work for any Linux image, you might just need to modify `apk add` to be `apt install` or something else. If you're using NodeJS in Docker, use `node:14-alpine` instead.
```dockerfile
FROM alpine:latest AS setup
```

Then we set the environment variable `RUNNING_IN_DOCKER` to true. I do this with all my Dockerfiles just because it's sometimes really useful to be able to tell easily if a script is running in Docker or on the host (e.g. testing automatically before a commit made in the container vs through VS Code on the host).
```dockerfile
ENV RUNNING_IN_DOCKER true
```

Next, we create a new unprivileged user called `main` for security (using `-D` to not give a password). Then we just create a a folder to put our app in, and give ownership of it to that user.
```dockerfile
RUN adduser -D main
RUN mkdir -p /app \
    && chown -R main:main /app
```
if you're using NodeJS, don't bother creating a new user, you already have the `node` user created for you by the NodeJS base image. That user also already has the setup to install packages without any extra config. Just get rid of the first line and change `main:main` to `node:node`.

After that, we install the dependencies for ZSH and then download [Antigen](https://github.com/zsh-users/antigen) (used for managing ZSH plugins) from GitHub.
```dockerfile
RUN apk --no-cache add zsh curl git
RUN mkdir -p /home/main/.antigen
RUN curl -L git.io/antigen > /home/main/.antigen/antigen.zsh
```

With Antigen installed, we copy over our ZSH configuration, which I like to store in `.dockershell.sh`, but it will be copied to `.zshrc` in the container. I have an example configuration in [this Gist](https://gist.github.com/arctic-hen7/bbfcc3021f7592d2013ee70470fee60b) if you need inspiration!
```dockerfile
COPY .dockershell.sh /home/main/.zshrc
```

Then we just give the `main` user ownership of both the Antigen config and `.zshrc`.
```dockerfile
RUN chown -R main:main /home/main/.antigen /home/main/.zshrc
```

Now, we can actually run ZSH, which will automatically pull everything we need to fulfill our configuration! This may take some time depending on how complex your config is, but after you've done it once, it's the same for every other container that ever uses it! Also note that you will have to rebuild whenever you change your ZSH config of course. Anyway, we run ZSH as the `main` user so it sets it up with the right permissions (that's the user we'll interact with the container with). After that, we switch back to `root` for any remaining stages.
```dockerfile
USER main
RUN /bin/zsh /home/main/.zshrc
USER root
```

And that'll give you ZSH in Docker! Just remember to add this when you want a prompt:
```dockerfile
ENTRYPOINT [ "/bin/zsh" ]
```

Here are three Gists that might help in getting everything operational:

- [Dockerfile with ZSH](https://gist.github.com/arctic-hen7/10987790b86360820e2790650e289f0b)
- [Dockerfile with ZSH for NodeJS](https://gist.github.com/arctic-hen7/2281c59d369df0a472cb0d457f6c47c8)
- [My ZSH config for Docker](https://gist.github.com/arctic-hen7/bbfcc3021f7592d2013ee70470fee60b)

Thanks for reading! Happy ZSHing! 👋