This document provides comprehensive instructions for deploying the complete TehtavaApp application (both backend and frontend) to an Azure Linux Virtual Machine using Nginx as a web server.
- Prerequisites
- Azure VM Setup
- Backend Deployment
- Frontend Deployment
- Nginx Configuration
- SSL Configuration
- Database Setup
- Service Configuration
- Azure DevOps Pipeline
- Troubleshooting
- Maintenance
Before starting the deployment process, ensure you have:
- An Azure account with an active subscription
- SSH access to your Azure VM
- Domain name (for SSL configuration)
- Azure DevOps organization and project (for CI/CD)
- Backend and frontend application code ready for deployment
-
Log in to the Azure Portal
-
Navigate to "Virtual machines" and click "Create"
-
Select "Azure virtual machine"
-
Configure the VM:
- Basics:
- Resource group: Create new or select existing
- VM name:
tehtavaapp-vm - Region: Select nearest region
- Availability options: No infrastructure redundancy required
- Security type: Standard
- Image: Ubuntu Server 20.04 LTS
- Size: Standard_B2s (2 vCPUs, 4 GiB memory) or higher
- Administrator account:
- Authentication type: SSH public key
- Username:
azureuser - SSH public key source: Generate new key pair or use existing
- Inbound port rules:
- Allow SSH (22), HTTP (80), and HTTPS (443)
- Basics:
-
Complete the VM creation process and wait for deployment to finish
Connect to your VM via SSH:
ssh azureuser@your-vm-ipUpdate the system and install required packages:
# Update package lists
sudo apt update
sudo apt upgrade -y
# Install .NET SDK
wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt update
sudo apt install -y apt-transport-https
sudo apt install -y dotnet-sdk-6.0
# Install Node.js and npm (for frontend builds if needed)
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt install -y nodejs
# Install Nginx
sudo apt install -y nginx
# Install Certbot for SSL
sudo apt install -y certbot python3-certbot-nginxCreate directories for the backend application:
sudo mkdir -p /var/www/tehtavaapp/api
sudo chown -R azureuser:azureuser /var/www/tehtavaappOn your development machine:
# Navigate to the backend project directory
cd path/to/tehtavaapp/Backend
# Publish the application
dotnet publish -c Release -o ./publishUse SCP to transfer the published files:
# From your development machine
cd path/to/tehtavaapp/Backend/publish
scp -r * azureuser@your-vm-ip:/var/www/tehtavaapp/api/On the VM:
sudo chown -R www-data:www-data /var/www/tehtavaapp/api
sudo chmod -R 755 /var/www/tehtavaapp/apiCreate or update the production settings file:
sudo nano /var/www/tehtavaapp/api/appsettings.Production.jsonAdd the following configuration:
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=TehtavaApp;User Id=TehtavaAppUser;Password=YourStrongPassword;TrustServerCertificate=True;"
},
"JwtSettings": {
"Key": "your-super-secret-key-with-at-least-32-characters",
"Issuer": "https://your-domain.com",
"Audience": "https://your-domain.com",
"ExpiryInDays": 7
},
"Cors": {
"AllowedOrigins": [
"https://your-domain.com"
]
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"FileStorage": {
"BasePath": "wwwroot/uploads",
"AllowedExtensions": [".pdf", ".doc", ".docx", ".txt", ".jpg", ".jpeg", ".png"],
"MaxFileSizeBytes": 10485760
}
}Create directories for the frontend application:
sudo mkdir -p /var/www/tehtavaapp/frontend
sudo chown -R azureuser:azureuser /var/www/tehtavaapp/frontendOn your development machine:
# Navigate to the frontend project directory
cd path/to/tehtavaapp/Frontend/tehtavaappfrontend
# Install dependencies
npm install
# Create production environment file
cat > .env.production << EOL
VITE_API_URL=https://your-domain.com
EOL
# Build the application
npm run buildUse SCP to transfer the built files:
# From your development machine
cd path/to/tehtavaapp/Frontend/tehtavaappfrontend/build
scp -r * azureuser@your-vm-ip:/var/www/tehtavaapp/frontend/On the VM:
sudo chown -R www-data:www-data /var/www/tehtavaapp/frontend
sudo chmod -R 755 /var/www/tehtavaapp/frontendCreate a new Nginx configuration file:
sudo nano /etc/nginx/sites-available/tehtavaappAdd the following configuration:
server {
listen 80;
server_name your-domain.com;
# Redirect HTTP to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name your-domain.com;
# SSL configuration will be added by Certbot
# Frontend
location / {
root /var/www/tehtavaapp/frontend;
try_files $uri $uri/ /index.html;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
}
# API - Fixed to prevent path duplication
location /api/ {
# Remove the /api prefix before proxying to prevent duplication
rewrite ^/api/(.*) /$1 break;
proxy_pass http://localhost:5001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# SignalR hub configuration
location /hubs/ {
proxy_pass http://localhost:5001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Static files
location /uploads/ {
alias /var/www/tehtavaapp/api/wwwroot/uploads/;
expires 30d;
add_header Cache-Control "public, no-transform";
}
# Error logs
error_log /var/log/nginx/tehtavaapp-error.log;
access_log /var/log/nginx/tehtavaapp-access.log;
}sudo ln -s /etc/nginx/sites-available/tehtavaapp /etc/nginx/sites-enabled/
sudo nginx -t # Test the configuration
sudo systemctl reload nginxsudo certbot --nginx -d your-domain.comFollow the prompts to complete the SSL configuration.
Visit https://your-domain.com to verify that SSL is working correctly for the frontend.
Visit https://your-domain.com/api/health to verify that SSL is working correctly for the backend.
# Import the public repository GPG keys
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
# Register the SQL Server Ubuntu repository
sudo add-apt-repository "$(wget -qO- https://packages.microsoft.com/config/ubuntu/20.04/mssql-server-2019.list)"
# Install SQL Server
sudo apt update
sudo apt install -y mssql-server
# Configure SQL Server
sudo /opt/mssql/bin/mssql-conf setup# Import the public repository GPG keys
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
# Register the Microsoft Ubuntu repository
curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list
# Install SQL Server tools
sudo apt update
sudo apt install -y mssql-tools unixodbc-dev
# Add SQL Server tools to PATH
echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc
source ~/.bashrc# Connect to SQL Server
sqlcmd -S localhost -U SA -P 'YourStrongPassword'
# Create database
CREATE DATABASE TehtavaApp;
GO
# Create login
CREATE LOGIN TehtavaAppUser WITH PASSWORD = 'YourStrongPassword';
GO
# Create user and grant permissions
USE TehtavaApp;
GO
CREATE USER TehtavaAppUser FOR LOGIN TehtavaAppUser;
GO
ALTER ROLE db_owner ADD MEMBER TehtavaAppUser;
GO
EXITCreate a service file for the backend application:
sudo nano /etc/systemd/system/tehtavaapp-api.serviceAdd the following configuration:
[Unit]
Description=TehtavaApp API
After=network.target
[Service]
WorkingDirectory=/var/www/tehtavaapp/api
ExecStart=/usr/bin/dotnet /var/www/tehtavaapp/api/TehtavaApp.API.dll
Restart=always
RestartSec=10
SyslogIdentifier=tehtavaapp-api
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
Environment=ASPNETCORE_URLS=http://localhost:5001
[Install]
WantedBy=multi-user.targetsudo systemctl enable tehtavaapp-api.service
sudo systemctl start tehtavaapp-api.service
sudo systemctl status tehtavaapp-api.serviceCreate a file named azure-pipelines.yml in your repository:
trigger:
branches:
include:
- main
variables:
vmUsername: 'azureuser'
vmIp: '$(VM_IP)'
domain: '$(DOMAIN)'
apiPort: '5001'
backendProjectPath: 'Backend'
frontendProjectPath: 'Frontend/tehtavaappfrontend'
stages:
- stage: Build
displayName: 'Build Stage'
jobs:
- job: BuildBackend
displayName: 'Build Backend'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
displayName: 'Use .NET SDK'
inputs:
packageType: 'sdk'
version: '6.0.x'
- task: DotNetCoreCLI@2
displayName: 'Restore Backend Dependencies'
inputs:
command: 'restore'
projects: '$(backendProjectPath)/*.csproj'
- task: DotNetCoreCLI@2
displayName: 'Build Backend'
inputs:
command: 'build'
projects: '$(backendProjectPath)/*.csproj'
arguments: '--configuration Release'
- task: DotNetCoreCLI@2
displayName: 'Publish Backend'
inputs:
command: 'publish'
publishWebProjects: false
projects: '$(backendProjectPath)/*.csproj'
arguments: '--configuration Release --output $(Build.ArtifactStagingDirectory)/backend'
zipAfterPublish: true
modifyOutputPath: false
- task: PublishBuildArtifacts@1
displayName: 'Publish Backend Artifacts'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)/backend'
ArtifactName: 'backend'
publishLocation: 'Container'
- job: BuildFrontend
displayName: 'Build Frontend'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Use Node.js'
inputs:
versionSpec: '16.x'
- task: Npm@1
displayName: 'Install Frontend Dependencies'
inputs:
command: 'install'
workingDir: '$(frontendProjectPath)'
- task: Bash@3
displayName: 'Create Environment File'
inputs:
targetType: 'inline'
script: |
# Create production environment file
echo "VITE_API_URL=https://$(domain)" > $(frontendProjectPath)/.env.production
# Display environment file for debugging
echo "Production Environment File:"
cat $(frontendProjectPath)/.env.production
- task: Npm@1
displayName: 'Build Frontend'
inputs:
command: 'custom'
workingDir: '$(frontendProjectPath)'
customCommand: 'run build'
- task: ArchiveFiles@2
displayName: 'Archive Frontend'
inputs:
rootFolderOrFile: '$(frontendProjectPath)/build'
includeRootFolder: false
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/frontend/frontend.zip'
replaceExistingArchive: true
- task: PublishBuildArtifacts@1
displayName: 'Publish Frontend Artifacts'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)/frontend'
ArtifactName: 'frontend'
publishLocation: 'Container'
- stage: Deploy
displayName: 'Deploy to Production'
dependsOn: Build
jobs:
- deployment: DeployProduction
displayName: 'Deploy to VM'
environment: 'production'
pool:
vmImage: 'ubuntu-latest'
strategy:
runOnce:
deploy:
steps:
- task: DownloadBuildArtifacts@0
displayName: 'Download Backend Artifacts'
inputs:
buildType: 'current'
downloadType: 'specific'
itemPattern: 'backend/**'
downloadPath: '$(System.ArtifactsDirectory)'
- task: DownloadBuildArtifacts@0
displayName: 'Download Frontend Artifacts'
inputs:
buildType: 'current'
downloadType: 'specific'
itemPattern: 'frontend/**'
downloadPath: '$(System.ArtifactsDirectory)'
- task: InstallSSHKey@0
displayName: 'Install SSH Key'
inputs:
knownHostsEntry: '$(KNOWN_HOSTS)'
sshPublicKey: '$(SSH_PUBLIC_KEY)'
sshKeySecureFile: 'vm_ssh_key'
- task: Bash@3
displayName: 'Deploy to VM'
inputs:
targetType: 'inline'
script: |
# Create deployment directories on VM
ssh $(vmUsername)@$(vmIp) "mkdir -p /tmp/api /tmp/frontend"
# Copy backend files to VM
scp $(System.ArtifactsDirectory)/backend/*.zip $(vmUsername)@$(vmIp):/tmp/api/backend.zip
# Copy frontend files to VM
scp $(System.ArtifactsDirectory)/frontend/*.zip $(vmUsername)@$(vmIp):/tmp/frontend/frontend.zip
# Execute deployment script on VM
ssh $(vmUsername)@$(vmIp) "bash -s" << 'EOF'
# Extract backend files
sudo mkdir -p /var/www/tehtavaapp/api
sudo unzip -o /tmp/api/backend.zip -d /var/www/tehtavaapp/api/
sudo chown -R www-data:www-data /var/www/tehtavaapp/api
# Extract frontend files
sudo mkdir -p /var/www/tehtavaapp/frontend
sudo unzip -o /tmp/frontend/frontend.zip -d /var/www/tehtavaapp/frontend/
sudo chown -R www-data:www-data /var/www/tehtavaapp/frontend
# Restart the service
sudo systemctl restart tehtavaapp-api.service
# Clean up temporary files
rm -f /tmp/api/backend.zip
rm -f /tmp/frontend/frontend.zip
EOFIn Azure DevOps, create the following variables:
VM_IP: Your VM's IP addressDOMAIN: Your domain nameKNOWN_HOSTS: The known_hosts entry for your VMSSH_PUBLIC_KEY: Your SSH public key
Upload your SSH private key as a secure file named vm_ssh_key in Azure DevOps.
Check the service logs:
sudo journalctl -u tehtavaapp-api.serviceTest the Nginx configuration:
sudo nginx -tVerify the connection string and SQL Server status:
sudo systemctl status mssql-serverCheck file permissions:
ls -la /var/www/tehtavaapp/api
ls -la /var/www/tehtavaapp/frontendCheck Certbot logs:
sudo certbot certificatesIf the frontend can't connect to the backend API, check:
- The
VITE_API_URLenvironment variable in the frontend build - CORS configuration in the backend
- Nginx configuration for the
/api/location
If you see 404 errors with paths like /api/api/..., check the Nginx configuration to ensure the rewrite rule is correctly removing the /api prefix:
location /api/ {
# Remove the /api prefix before proxying to prevent duplication
rewrite ^/api/(.*) /$1 break;
proxy_pass http://localhost:5001;
# ... other settings ...
}sudo apt update
sudo apt upgrade -yCertbot will automatically renew certificates, but you can test the renewal process:
sudo certbot renew --dry-run# Create a backup directory
sudo mkdir -p /var/backups/tehtavaapp
# Backup the database
sudo -u mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P 'YourStrongPassword' -Q "BACKUP DATABASE TehtavaApp TO DISK = N'/var/backups/tehtavaapp/tehtavaapp_$(date +%Y%m%d).bak' WITH NOFORMAT, NOINIT, NAME = 'TehtavaApp-Full Database Backup', SKIP, NOREWIND, NOUNLOAD, STATS = 10"# Check application logs
sudo journalctl -u tehtavaapp-api.service
# Check Nginx logs
sudo tail -f /var/log/nginx/tehtavaapp-error.log
sudo tail -f /var/log/nginx/tehtavaapp-access.logTo update the application:
- Build new versions of the backend and frontend
- Deploy the new versions using the same process as the initial deployment
- Restart the backend service:
sudo systemctl restart tehtavaapp-api.service
This deployment guide provides comprehensive instructions for deploying the complete TehtavaApp application to an Azure Linux VM. For additional support or questions, please contact the development team.