Installation on Debian Linux
1. Install Certbot
Code:
sudo apt-get install certbot python3-certbot-nginx
Code:
sudo apt-get install certbot python3-certbot-apache
Code:
sudo apt-get install certbot -y
(You can replace apt-get with apt on Ubuntu)
2. Fetching a Certificate (producing fullchain.pem, privkey.pem, cert.pem, chain.pem)
Code:
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Code:
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
or skip user prompts by providing your email and agreeing to the Terms of Service directly in the flag.
Code:
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com --email [email protected] --agree-tos --non-interactive
Code:
sudo certbot certonly --standalone -d yourdomain.com -d www.yourdomain.com --email [email protected] --agree-tos --non-interactive
Run Certbot in standalone mode. It will instantly spin up a temporary server on Port 80, grab the certificates, and close itself out.
Note: Make sure your firewall allows incoming traffic on
Port 80 for this step to succeed.
Certbot downloads the actual certificates to /etc/letsencrypt/archive/yourdomain.com/. However, it creates
symbolic links pointing to the newest versions in /etc/letsencrypt/live/yourdomain.com/
3. Automating Renewal
Bash:
sudo certbot renew --dry-run
Certbot never overwrites old certificates. Instead, it downloads the new files into /etc/letsencrypt/archive/yourdomain.com/ and increments the number at the end of the filename.
- Old files: cert1.pem, priv1.pem, fullchain1.pem
- New files: cert2.pem, priv2.pem, fullchain2.pem
3.1. Updated Symbolic Links
Once the new files are downloaded, Certbot automatically updates the symbolic links inside /etc/letsencrypt/live/yourdomain.com/.
- The links (fullchain.pem, privkey.pem, etc.) are seamlessly shifted to point to the newest archived files (e.g., fullchain2.pem).
- Because Nginx points to the live directory, you do not need to update your Nginx configuration file.
3.2. Static Configuration Files
The global security files (options-ssl-nginx.conf and ssl-dhparams.pem) are
not redownloaded or changed during a renewal. They remain exactly as they were.
3.3. Automatic Nginx Reload
Certbot will automatically reload Nginx after a successful renewal so the web server can read the new files. It does this without dropping active user connections.
4. Export Let's Encrypt certificates into a password-protected
PKCS#12 keystore format (.p12) for B4J Server
Bash:
openssl pkcs12 -export -out /home/keystore.p12 -in /etc/letsencrypt/live/yourdomain.com/fullchain.pem -inkey /etc/letsencrypt/live/yourdomain.com/privkey.pem -name jetty
or without password prompt
Bash:
sudo openssl pkcs12 -export -out /home/keystore.p12 -in /etc/letsencrypt/live/yourdomain.com -inkey /etc/letsencrypt/live/yourdomain.com -name jetty -passout pass:your_keystore_password
5. ConfigureSSL in jServer
B4X:
Dim ssl As SslConfiguration
ssl.Initialize
ssl.SetKeyStorePath("/etc/letsencrypt/live/yourdomain.com", "keystore.p12")
ssl.KeyStorePassword = "your_keystore_password"
srvr.SetSslConfiguration(ssl, 51041)
6. Automated Renewal Script
Code:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/update-b4j.sh << 'EOF'
#!/bin/bash
# 1. Path variables (Adjust these to match your actual server setup!)
JAR_PATH="/home/myb4japp/your_app_name.jar"
LOG_PATH="/home/myb4japp/nohup.out"
KEYSTORE_PATH="/home/keystore.p12"
DOMAIN="yourdomain.com"
PASSWORD="your_secret_password"
# 2. Re-generate the PKCS12 file with the newly renewed certificates
openssl pkcs12 -export -out "$KEYSTORE_PATH" \
-in /etc/letsencrypt/live/"$DOMAIN"/fullchain.pem \
-inkey /etc/letsencrypt/live/"$DOMAIN"/privkey.pem \
-name b4j -passout pass:"$PASSWORD"
# 3. Find and kill the existing running B4J process safely
# This looks for the unique .jar filename to avoid killing other java apps
PID=$(pgrep -f "java.*$JAR_PATH")
if [ -n "$PID" ]; then
echo "Stopping existing B4J process ($PID)..."
kill "$PID"
sleep 3 # Give it a few seconds to release the ports safely
else
echo "No running B4J process found to stop."
fi
# 4. Restart the B4J app in the background using nohup
echo "Starting B4J application via nohup..."
cd "$(dirname "$JAR_PATH")"
nohup java -jar "$JAR_PATH" > "$LOG_PATH" 2>&1 &
echo "B4J update and restart completed successfully!"
EOF
Remember to make the script executable.
Code:
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/update-b4j.sh

Important Considerations for nohup
- File paths must be absolute: Always use full explicit paths (like /home/myb4japp/your_app_name.jar) in scripts run by Certbot, as cron environments do not know your user's relative paths. [1]
- Temporary Down Time: Your server will experience roughly 3 seconds of downtime while the old Java process closes and the new one spins up to read the fresh keystore file.
- Working Directory: The script includes a cd command right before launching nohup. This ensures that any relative paths inside your B4J app code (like checking for local .db or asset folders) do not break.
This script is executed automatically by
Certbot’s built-in renewal system via a system timer or cron job. You do not need to schedule or trigger the script yourself.
Test the script: You can manually run the script once right now to verify it handles the keystore generation and successfully restarts your app without errors:
Bash:
sudo /etc/letsencrypt/renewal-hooks/deploy/update-b4j.sh
Verify it is Set Up Correctly
You can test the entire pipeline right now—including checking if Certbot discovers your script and executes it without errors—by performing a
dry run simulation.
Bash:
sudo certbot renew --dry-run
If everything is configured properly, the logs will simulate the renewal process and output a section at the bottom similar to this:
Bash:
Dry run count: 1
Simulating renewal of an existing certificate for yourdomain.com
Running deploy-hook command: /etc/letsencrypt/renewal-hooks/deploy/update-b4j.sh