Last active
April 30, 2026 05:38
-
-
Save karthiks/df6787aa7a345c6eb88411495eaf1cbe to your computer and use it in GitHub Desktop.
Dockerfile to setup dev environment for Claude Code to build Expo / React Native app
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # --- Stage 1: The JDK Provider --- | |
| FROM eclipse-temurin:17-jdk-jammy AS jdk-source | |
| # --- Stage 2: Final Development Image --- | |
| FROM node:20-slim | |
| # Build arguments for user configuration | |
| ARG USERNAME=ccagent | |
| ARG WORKDIR=app | |
| # Environment variables | |
| ENV NODE_USER=$USERNAME | |
| ENV JAVA_HOME=/opt/java/openjdk | |
| ENV ANDROID_HOME=/home/$USERNAME/android-sdk | |
| ENV PATH="/home/$USERNAME/.local/bin:${JAVA_HOME}/bin:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH}" | |
| # ============================================ | |
| # Step 1: Install Minimal System Dependencies | |
| # Watchman is needed for RN | |
| # Leverage OMZsh with zsh shell instead of Bash for productivity | |
| # ripgrep is better grep utility that respects .gitignore, .claudeignore and stuff. | |
| # `usbutils` and `android-tools-adb` required for connecting emulator/devices to expo server in Docker | |
| # ============================================ | |
| RUN apt-get update && apt-get install -y \ | |
| curl wget unzip watchman git procps \ | |
| sudo zsh ripgrep vim jq ca-certificates \ | |
| usbutils android-tools-adb \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # ============================================ | |
| # Step 2: Setup Java | |
| # ============================================ | |
| # Copy OpenJDK from Stage 1 (saves ~400MB of apt-install overhead) | |
| COPY --from=jdk-source $JAVA_HOME $JAVA_HOME | |
| # ============================================ | |
| # Step 3: User Configuration | |
| # Rename existing user 'node' to $USERNAME to maintain UID 1000 | |
| # groupmod -n, --new-name NEW_GROUP: to rename a group | |
| # usermod -l, --login NEW_LOGIN: to rename user account | |
| # usermod -m, --move-home: Moves the contents of the current home directory to the new directory (used with -d). | |
| # usermod -d, --home HOME_DIR: Changes the user's home directory to a new path. | |
| # usermod -s, --shell SHELL: Changes the user’s login shell (e.g., /bin/bash or /sbin/nologin). | |
| # ============================================ | |
| RUN groupmod -n $USERNAME node \ | |
| && usermod -l $USERNAME -m -d /home/$USERNAME node \ | |
| # Add user to sudoers with NOPASSWD | |
| && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ | |
| && chmod 0440 /etc/sudoers.d/$USERNAME \ | |
| && mkdir -p $ANDROID_HOME \ | |
| && chown -R $USERNAME:$USERNAME $ANDROID_HOME | |
| # ============================================ | |
| # Step 4: System Prep (Root) | |
| # ============================================ | |
| # Set zsh as the system default for the non-root user (adjust user name as needed) | |
| RUN chsh -s $(which zsh) $USERNAME && \ | |
| mkdir -p /$WORKDIR && chown $USERNAME:$USERNAME /$WORKDIR | |
| # ============================================ | |
| # Step 5: Grouped Android SDK Minimal Installation (As User) | |
| # We create the dir and change ownership so the user can manage it later | |
| # ============================================ | |
| USER $USERNAME | |
| WORKDIR /home/$USERNAME | |
| RUN mkdir -p $ANDROID_HOME/cmdline-tools \ | |
| && wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O /tmp/tools.zip \ | |
| && unzip /tmp/tools.zip -d $ANDROID_HOME/cmdline-tools \ | |
| && mv $ANDROID_HOME/cmdline-tools/cmdline-tools $ANDROID_HOME/cmdline-tools/latest \ | |
| && rm /tmp/tools.zip \ | |
| && yes | sdkmanager --licenses \ | |
| && sdkmanager "platform-tools" "platforms;android-35" "build-tools;35.0.0" | |
| # ============================================ | |
| # Step 6: Setup Workspace | |
| # ============================================ | |
| USER $USERNAME | |
| WORKDIR /$WORKDIR | |
| # If step-4 fails, this should fix it | |
| # Create a small bashrc that hands interactive bash sessions over to zsh | |
| RUN echo 'if [ -t 1 ]; then\n exec /bin/zsh\nfi' > /home/$USERNAME/.bashrc | |
| # ============================================ | |
| # Step 7: Install Productivity Tools (Oh My Zsh & Shell Config) | |
| # Since you volume mount your project from host into /$WORKDIR, the file ownership inside the container might appear as 'root'. Thus, | |
| # it is often safer to use a wildcard like '*' so Git doesn't block you if you mount multiple projects or submodules. | |
| # ============================================ | |
| RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended \ | |
| # Ensure our ENV PATH is preserved in interactive zsh sessions | |
| && echo 'export PATH=$PATH' >> ~/.zshrc \ | |
| && git config --global core.fileMode false \ | |
| && git config --global --add safe.directory '*' \ | |
| # Add a helper alias for headless ADB connection | |
| && echo 'alias adb-sync="adb connect host.docker.internal:5555"' >> /home/$USERNAME/.zshrc | |
| # ============================================ | |
| # Step 8: Install npm global packages | |
| # ============================================ | |
| # Configure npm to install global packages in user's home directory | |
| RUN mkdir -p ~/.local/bin \ | |
| && npm config set prefix ~/.local \ | |
| && npm install -g npm@latest \ | |
| # Install EAS CLI (via NPM - Required for Expo workflow), TypeScript and the Language Server globally | |
| && npm install -g eas-cli typescript typescript-language-server | |
| # ============================================ | |
| # Step 9: Install Claude Code NATIVE (Recommended for 2026) | |
| # ============================================ | |
| RUN curl -fsSL https://claude.ai/install.sh | bash | |
| # ============================================ | |
| # Step 10: Set Default User and Entrypoint | |
| # ============================================ | |
| USER $USERNAME | |
| WORKDIR /$WORKDIR | |
| # Copy the health-check script | |
| COPY --chown=$USERNAME:$USERNAME ./scripts/healthcheck.sh /usr/local/bin/healthcheck | |
| RUN chmod +x /usr/local/bin/healthcheck | |
| # Not fit for this dev environment. | |
| #ENTRYPOINT ["zsh", "-i"] | |
| # The container will stay alive so you can exec into it on multiple tabs (helpful for RN dev workflow) | |
| CMD ["tail", "-f", "/dev/null"] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment