BillionMail icon indicating copy to clipboard operation
BillionMail copied to clipboard

How can I build my own billionmail/core image?

Open namchivas2k opened this issue 6 months ago • 2 comments

How can I build a custom image of billionmail/core myself? I want to customize some parts of the UI as well as the API, but I don't know how to build the image.

namchivas2k avatar Jun 02 '25 09:06 namchivas2k

This is simple way to customize your BillionMail.

  1. Setup Golang developmenr environment in your server.
  2. Change the codes and compile.
  3. Use docker cp command to replace container billionmail–core–billionmail-1:/opt/billionmail/core/billionmail binary file.
  4. Restart cotainer to check it.

dreambladeflag avatar Jun 03 '25 15:06 dreambladeflag

Hi,

There may be a better approach than this, but here’s how I managed to rebuild billionmail/core locally and apply my UI changes. These steps assume you’re using Docker Compose.


  1. Modify docker-compose.yml

In your BillionMail/docker-compose.yml, locate the core-billionmail service and replace its image: line with a build: block. For example:

  core-billionmail:  # BillionMail management panel and Roundcube frontend
    # image: billionmail/core:1.5
    build:
      context: .
      dockerfile: ./Dockerfiles/core/Dockerfile

This tells Docker Compose to build the image from your local Dockerfiles/core/Dockerfile rather than pulling billionmail/core:1.5 from Docker Hub.


  1. Create or replace Dockerfiles/core/Dockerfile

Create a new BillionMail/Dockerfiles/core/Dockerfile (or overwrite the existing one) with the following content:

FROM alpine:3.20

LABEL maintainer="https://github.com/aaPanel/BillionMail"

# Set environment variables
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8

ARG TARGETARCH

# Copy the prebuilt core binary and static assets
COPY core/billionmail-${TARGETARCH} /opt/billionmail/core/billionmail
COPY core/manifest            /opt/billionmail/core/manifest
COPY core/languages           /opt/billionmail/core/languages
COPY core/public              /opt/billionmail/core/public
COPY core/resource            /opt/billionmail/core/resource

# Copy custom scripts
COPY ./Dockerfiles/core/stop-supervisor.sh     /stop-supervisor.sh
COPY ./Dockerfiles/core/core.sh                /core.sh
COPY ./Dockerfiles/core/restart_fail2ban.sh    /restart_fail2ban.sh
COPY ./Dockerfiles/core/supervisord.conf /etc/supervisor/supervisord.conf

# Install required packages
RUN apk add --no-cache \
      bash \
      ca-certificates \
      curl \
      supervisor \
      rsyslog \
      tzdata \
      busybox-extras \
      postgresql-client \
      fail2ban \
      iptables \
      ipset \
    && rm -rf /var/cache/apk/* \
    && chmod +x /stop-supervisor.sh /core.sh /restart_fail2ban.sh /opt/billionmail/core/billionmail

ENTRYPOINT ["/core.sh"]
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

  1. Build the billionmail core binary

By default, the core/ directory does not contain the compiled billionmail executable. To generate it, run the same commands as in BillionMail/core/go-build.sh. For example:

# From the project root (where docker-compose.yml lives)
cd /opt/BillionMail

# Start an Alpine container with the code mounted for compilation
docker run -d \
  --name p-g-alpine \
  --hostname p-g-alpine \
  --restart=always \
  -v "$(pwd)/core:/opt/core" \
  -v "$(pwd)/Dockerfiles/core/repositories:/etc/apk/repositories" \
  alpine:3.20 tail -f /dev/null

# Enter the container shell
docker exec -it p-g-alpine sh

# Inside the container, navigate to /opt/core and run the build script
cd /opt/core
sh go-build.sh

After go-build.sh finishes, you should see core/billionmail-<arch> appear in your local core/ folder. You can then exit the container:

exit
docker stop p-g-alpine
docker rm p-g-alpine

  1. Customize the frontend (UI)

  2. Modify UI source files Edit the frontend code you need.

  3. Create a deploy.js script After building the frontend, you need to copy the compiled assets into core/public/dist. Create billionmail/core/frontend/deploy.js with:

    // deploy.js
    
    import fs from 'fs';
    import path from 'path';
    import fse from 'fs-extra';
    import { fileURLToPath } from 'url';
    
    // Polyfill __dirname
    const __filename = fileURLToPath(import.meta.url);
    const __dirname = path.dirname(__filename);
    
    const srcDir = path.resolve(__dirname, 'dist');
    const destDir = path.resolve(__dirname, '../public/dist');
    
    // Remove old dist folder, if it exists
    if (fs.existsSync(destDir)) {
      fse.removeSync(destDir);
    }
    
    // Copy new build artifacts
    fse.copySync(srcDir, destDir);
    
    console.log('Copied build artifacts to:');
    console.log(destDir);
    
  4. Add SERVER_NAME to environment files

    SERVER_NAME=192.168.66.66
    
  5. Install dependencies and build In your local environment (with Node.js + pnpm installed), run:

    cd billionmail/core/frontend
    pnpm install
    pnpm add fs-extra          # ‹deploy.js› relies on fs-extra
    pnpm build
    

    At this point, core/public/dist contains your customized UI.


  1. Rebuild and start the core-billionmail service

  2. Return to the project root (where your docker-compose.yml is located):

    cd /path/to/BillionMail
    
  3. Build the core-billionmail image (no cache):

    docker compose build --no-cache core-billionmail
    
  4. Start the containers:

    docker compose up -d
    

zan-12 avatar Jun 03 '25 15:06 zan-12