# Joplin Server Sync Issues

## The Problem

Joplin desktop or mobile app won't sync with your self-hosted Joplin Server. Common symptoms:

```
Completed: 25/02/2026 10:30
Last error: Error: ECONNREFUSED 127.0.0.1:22300
```

```
Sync failed: Could not connect to Joplin Server. Please check the Synchronisation options.
```

Or sync starts but items fail with 401/403 errors, notes don't appear on other devices, or sync gets stuck showing "Synchronizing..." indefinitely.

## The Cause

Joplin Server sync requires the desktop/mobile client to reach the server over HTTPS, authenticate successfully, and have matching sync target configuration. The most common failure points:

1. **Wrong sync target URL in the client** — must include the full path and use the external URL
2. **Sync target type not set to "Joplin Server"** — Joplin defaults to Dropbox, not self-hosted
3. **Reverse proxy not forwarding correctly** — WebSocket or large request blocking
4. **PostgreSQL connection issues** — server can't reach its database
5. **SSL certificate problems** — self-signed certs rejected by the client

## The Fix

### Method 1: Configure the Client Correctly (Most Common)

In the Joplin desktop app, go to **Tools → Options → Synchronization** and set:

| Setting | Value |
|---------|-------|
| Synchronization target | **Joplin Server** (not Joplin Cloud, not WebDAV) |
| Joplin Server URL | `https://joplin.example.com` |
| Joplin Server email | Your user email (the one you created in the admin panel) |
| Joplin Server password | Your user password |

**Common mistakes:**

| Wrong | Right |
|-------|-------|
| `http://192.168.1.100:22300` | `https://joplin.example.com` (use the external URL) |
| Sync target: "Joplin Cloud" | Sync target: "Joplin Server" |
| Sync target: "WebDAV" | Sync target: "Joplin Server" |
| URL with trailing slash: `https://joplin.example.com/` | `https://joplin.example.com` (no trailing slash) |

After changing settings, click **Check synchronization configuration** to test the connection.

### Method 2: Verify the Server Is Running

Check that Joplin Server is healthy:

```bash
# Check container status
docker compose ps

# Check server logs for errors
docker compose logs joplin-server --tail=50

# Test the API endpoint
curl -v https://joplin.example.com/api/ping
# Should return: {"status":"ok","message":"Joplin Server is running"}
```

If the container is restarting, check the logs for database connection errors (see Method 4).

### Method 3: Fix Reverse Proxy Configuration

Joplin Server requires WebSocket support and handles large sync payloads. Your reverse proxy must allow both.

**Nginx Proxy Manager:** Ensure WebSocket support is enabled in the proxy host settings.

**Nginx manual config:**
```nginx
server {
    listen 443 ssl;
    server_name joplin.example.com;

    client_max_body_size 200M;  # Required for large note attachments

    location / {
        proxy_pass http://127.0.0.1:22300;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
```

**Caddy:**
```
joplin.example.com {
    reverse_proxy localhost:22300
}
```

Caddy handles WebSocket and large bodies automatically.

**Key checks:**
- `client_max_body_size` (Nginx) must be large enough for attachments (200M recommended)
- WebSocket upgrade headers must be forwarded
- The proxy must not timeout long sync operations (set `proxy_read_timeout 300s;`)

### Method 4: Fix PostgreSQL Connection

If the server logs show database errors:

```
Error: connect ECONNREFUSED 127.0.0.1:5432
error: password authentication failed for user "joplin"
```

Verify your database configuration:

```yaml
services:
  joplin-server:
    image: joplin/server:3.5.2
    environment:
      APP_BASE_URL: https://joplin.example.com
      APP_PORT: 22300
      DB_CLIENT: pg
      POSTGRES_HOST: joplin-db        # Must match the PostgreSQL service name
      POSTGRES_PORT: 5432
      POSTGRES_DATABASE: joplin
      POSTGRES_USER: joplin
      POSTGRES_PASSWORD: strongpass    # Must match the database service
    depends_on:
      joplin-db:
        condition: service_healthy
    restart: unless-stopped

  joplin-db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: joplin
      POSTGRES_PASSWORD: strongpass
      POSTGRES_DB: joplin
    volumes:
      - joplin-db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U joplin"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
```

Ensure `POSTGRES_HOST` is the **service name** (`joplin-db`), not `localhost`.

### Method 5: Fix SSL Certificate Issues

If the client shows SSL errors or sync fails with "certificate" in the error:

**Self-signed certificates:** The Joplin desktop app may reject self-signed certs. Go to **Tools → Options → Synchronization → Advanced → Ignore TLS certificate errors** and enable it (development only).

**Let's Encrypt:** If using a reverse proxy with Let's Encrypt, ensure the cert covers `joplin.example.com`. Test with:

```bash
curl -I https://joplin.example.com
# Check for: HTTP/2 200 and valid certificate
```

**Internal network without SSL:** If Joplin Server is only accessible on your LAN, use `http://` in the sync URL and set `APP_BASE_URL` to match. However, sync over unencrypted HTTP is not recommended.

### Method 6: Create a User Account

The default admin account is for server management, not syncing. Create a dedicated user:

1. Open `https://joplin.example.com` in a browser
2. Log in with the admin credentials (default: `admin@localhost` / `admin`)
3. Go to **Admin → Users → Add User**
4. Create a user with an email and password
5. Use this email and password in the Joplin desktop app sync settings

If you're trying to sync with the admin account and it fails, create a regular user instead.

## Prevention

- Always use the external HTTPS URL (not `localhost:22300`) in the client sync settings
- Set the sync target to "Joplin Server" — not "Joplin Cloud" or "WebDAV"
- Add a healthcheck on PostgreSQL so the server waits for the database
- Set `client_max_body_size` to 200M+ in your reverse proxy
- Test sync with `Check synchronization configuration` after any config change
- Create a regular user for syncing — don't use the admin account

## Related

- [How to Self-Host Joplin Server](/apps/joplin-server)
- [Best Self-Hosted Note-Taking Apps](/best/note-taking)
- [Trilium vs Joplin](/compare/trilium-vs-joplin)
- [Reverse Proxy Setup](/foundations/reverse-proxy-explained)
- [Replace Evernote](/replace/evernote)
