Here're some of the project's best features:
- User Roles
- Multi Platform
- PWA
- Desktop Notifications
- Net 8
- Razor
- PostgreSQL
- SignalR
- Javascript
- Bootstrap
You can run the project without User Secrets by defining environment variables.
Quick start with .env file:
- Copy
.env.exampleto.envand complete values. - Start using one of the helper scripts:
PowerShell (Windows):
./scripts/start-dev.ps1Bash (Linux/macOS):
chmod +x ./scripts/start-dev.sh
./scripts/start-dev.shRequired:
- ConnectionStrings__LocalConnection
Firebase credentials (path-only pattern):
- Firebase__CredentialsPath
- GOOGLE_APPLICATION_CREDENTIALS
Authentication variables used by the app:
- Authentication__ValidIssuer
- Authentication__Audience
- Authentication__TokenUri
- Authentication__TokenCode
- Authentication__TokenReset
PowerShell (Windows):
$env:ConnectionStrings__LocalConnection = "Host=localhost;Port=5432;Database=turnero;Username=turnero;Password=turnero_pwd"
$env:Firebase__CredentialsPath = "C:\secrets\firebase.json"
$env:Authentication__ValidIssuer = "https://securetoken.google.com/your-project"
$env:Authentication__Audience = "your-project"
dotnet runBash (Linux/macOS):
export ConnectionStrings__LocalConnection="Host=localhost;Port=5432;Database=turnero;Username=turnero;Password=turnero_pwd"
export Firebase__CredentialsPath="/run/secrets/firebase.json"
export Authentication__ValidIssuer="https://securetoken.google.com/your-project"
export Authentication__Audience="your-project"
dotnet runNote:
- In Development, User Secrets still works as an optional fallback.
- Inline JSON credentials in environment variables are disabled to reduce secret exposure risk.
This setup runs only the web app in Docker and keeps PostgreSQL outside Docker.
Recommended TLS approach:
- Prefer a reverse proxy such as Nginx or Traefik in front of the app.
- Avoid baking Let's Encrypt certificates into the image.
- If you want Kestrel to terminate TLS directly, use docker-stack.tls.yml and mount the host certificate directory read-only.
- Ensure
.envhasConnectionStrings__PostgresConnectionwith your current external DB connection string. - Provide a firebase service account file path through
FIREBASE_CREDENTIALS_FILE.
Windows PowerShell:
$env:FIREBASE_CREDENTIALS_FILE = "D:/UserSecrets/aspnet-Turnero-1D8EA02B-D124-439A-B5F8-DE2044EFFABA/firebase.json"
docker compose up --buildLinux/macOS Bash:
export FIREBASE_CREDENTIALS_FILE="/opt/secrets/firebase.json"
docker compose up --buildApplication URL:
Production compose (healthcheck + resource limits):
$env:FIREBASE_CREDENTIALS_FILE = "D:/UserSecrets/aspnet-Turnero-1D8EA02B-D124-439A-B5F8-DE2044EFFABA/firebase.json"
docker compose -f docker-compose.prod.yml up --build -dexport FIREBASE_CREDENTIALS_FILE="/opt/secrets/firebase.json"
docker compose -f docker-compose.prod.yml up --build -dDirect TLS stack with mounted Let's Encrypt certs:
export LETSENCRYPT_DOMAIN="vps-1821822-x.dattaweb.com"
export FIREBASE_CREDENTIALS_FILE="/opt/secrets/firebase.json"
docker stack deploy -c docker-stack.tls.yml turneroYou can also set LETSENCRYPT_DOMAIN in .env and let the deploy script load it automatically.
Notes for direct TLS:
- The certificate directory must exist on every Swarm node that can run the task.
- The app listens on
443and8080in that stack. - Healthcheck uses
curl -kbecause the certificate is validated against the real domain, notlocalhost.
For true rolling updates (keep one instance running while the next starts), use Docker Swarm with docker-stack.prod.yml.
Initial setup (Linux server):
docker swarm init
export FIREBASE_CREDENTIALS_FILE="/opt/secrets/firebase.json"
docker build -t turnero-app:prod .
docker stack deploy -c docker-stack.prod.yml turneroUpdate to a new version without downtime:
docker build -t turnero-app:prod-v3.0.2 .
export TURNERO_IMAGE="turnero-app:prod-v3.0.2"
export FIREBASE_CREDENTIALS_FILE="/opt/secrets/firebase.json"
docker stack deploy -c docker-stack.prod.yml turneroVerify rollout:
docker service ls
docker service ps turnero_turnero-appOne-command deploy script:
chmod +x ./scripts/deploy.sh
./scripts/deploy.sh v3.0.2 /opt/secrets/firebase.jsonOptional env vars:
STACK_NAME(default:turnero)STACK_FILE(default:docker-stack.prod.yml)IMAGE_REPO(default:turnero-app)FIREBASE_CREDENTIALS_FILE(if you prefer not to pass arg2)
Deploy from Windows to Linux over SSH:
./scripts/deploy-remote.ps1 -RemoteHost "your-server" -User "deploy" -Version "v3.0.2" -RemotePath "/opt/turnero" -FirebaseCredentialsFile "/opt/secrets/firebase.json" -SshPort 2222This command now builds the Docker image locally, transfers it to the remote host (docker save | docker load), and runs docker stack deploy remotely.
After deploy, the script prints the published ports for the target service.
Prerequisites:
- Local Docker daemon must be running.
- Remote host must be a Docker Swarm manager node.
Sync .env only when changed (hash comparison):
./scripts/deploy-remote.ps1 -RemoteHost "your-server" -User "deploy" -Version "v3.0.2" -RemotePath "/opt/turnero" -FirebaseCredentialsFile "/opt/secrets/firebase.json" -SyncEnvSync .env with automatic remote backup before replace:
./scripts/deploy-remote.ps1 -RemoteHost "your-server" -User "deploy" -Version "v3.0.2" -RemotePath "/opt/turnero" -FirebaseCredentialsFile "/opt/secrets/firebase.json" -SyncEnv -BackupEnvBackup retention example (keep last 20 backups):
./scripts/deploy-remote.ps1 -RemoteHost "your-server" -User "deploy" -Version "v3.0.2" -RemotePath "/opt/turnero" -FirebaseCredentialsFile "/opt/secrets/firebase.json" -SyncEnv -BackupEnv -BackupEnvRetention 20Optional params:
-StackName(default:turnero)-StackFilePath(default:docker-stack.prod.yml)-UseTls(usesdocker-stack.tls.ymlunless-StackFilePathis explicitly provided)-ImageRepo(default:turnero-app)-SshKeyPath(for key-based auth)-SshPort(default:22)-SyncEnv(copies.envonly if content changed)-EnvFilePath(default:.env)-BackupEnv(creates remote backup:.env.bak.YYYYMMDDHHMMSSbefore overwrite)-BackupEnvRetention(default:10, use0to disable pruning)
TLS deploy example (binds Let's Encrypt certs from host):
./scripts/deploy-remote.ps1 -RemoteHost "your-server" -User "deploy" -Version "v3.0.2" -RemotePath "/opt/turnero" -FirebaseCredentialsFile "/opt/secrets/firebase.json" -SyncEnv -UseTlsFor -UseTls, .env must include LETSENCRYPT_DOMAIN, and on the remote host this path must exist:
/etc/letsencrypt/live/<LETSENCRYPT_DOMAIN>/fullchain.pem/etc/letsencrypt/live/<LETSENCRYPT_DOMAIN>/privkey.pem
Note: docker-stack.tls.yml mounts /etc/letsencrypt read-only (not only /etc/letsencrypt/live/<domain>), because Let's Encrypt files in live/ are commonly symlinks to archive/.
Environment variable fallback for SSH port:
DEPLOY_SSH_PORT(preferred)SSH_PORT(fallback)
The stack is configured with:
replicas: 2update_config.parallelism: 1update_config.order: start-firstfailure_action: rollback- Resource limits are intentionally low so the stack can run on a single-CPU host.
- Set
APP_PUBLISHED_PORTto avoid conflicts when another stack already uses the same host port. - For the TLS stack, set
APP_HTTPS_PORTif you need a non-standard published HTTPS port.
This project is licensed under the MIT License
If you have any questions or comments about the application you can contact the author through the following email address: pabloeferreyra@gmail.com.